/* An accepted second partner, and what she has told you about her timing.
   Both sit under the crew card's bond note. */
.player-bond-note.is-open { color: #c9a2ff; }

/* A standing promise of exclusivity. Deliberately the calmest colour on the
   card — it is the one line that means nothing is currently moving. When it
   starts to strain it warms toward the jealousy palette, so the card tells you
   it is going before the deck does. */
.player-bond-note.is-pact {
    color: #9fe3c0;
    background: rgba(159, 227, 192, 0.10);
    border-left-color: #9fe3c0;
}
.player-bond-note.is-pact.is-strained {
    color: #ffb27a;
    background: rgba(255, 178, 122, 0.12);
    border-left-color: #ff8b4a;
}
.player-cycle-note {
    margin-top: 3px; font-size: 10.5px; font-style: italic;
    color: #8fb7d4; line-height: 1.35;
}

/* ==========================================================================
   Marea Alta - "Open Water" Cinematic Design System
   ========================================================================== */

@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Outfit:wght@400;500;600;700;800&family=Cinzel:wght@500;600;700;800&display=swap');

:root {
    color-scheme: dark;

    --bg-void: #05070b;
    --bg-deep: #0a0d16;
    --card-bg: rgba(20, 20, 28, 0.55);
    --card-bg-solid: rgba(15, 15, 21, 0.94);
    --border-soft: rgba(255, 255, 255, 0.08);
    --border-glow: rgba(156, 43, 78, 0.22);

    --ember: #e8703a;
    --ember-soft: #f2a672;
    --wine: #9c2b4e;
    --frost: #8fc7e8;
    --gold: #e6c065;
    --amber: #ffab3d;
    --amber-soft: #ffd28a;
    --danger: #ff6b60;

    /* Text ramp — every step verified against --bg-void for WCAG AA.
       The old --text-dim (#6f6a60) sat at 3.5:1 and failed outright. */
    --text-main: #f4efe6;   /* ~16.2:1 */
    --text-muted: #b8ada0;  /*  ~8.8:1 */
    --text-dim: #8f8578;    /*  ~5.3:1 */

    --focus-ring: #8fc7e8;

    --shadow-glow: 0 0 20px rgba(232, 112, 58, 0.14);
    --shadow-btn: 0 0 15px rgba(232, 112, 58, 0.3);

    --radius-sm: 8px;
    --radius-md: 14px;
    --radius-lg: 22px;

    --ease-pop: cubic-bezier(.18, .89, .32, 1.28);
    --ease-smooth: cubic-bezier(.4, 0, .2, 1);

    /* Overwritten by settings.js */
    --text-scale: 100;
    --line-base: 1.6;

    /* --- Fluid shell metrics -------------------------------------------
       Everything structural is expressed against the viewport rather than in
       fixed pixels, because browser zoom IS a viewport change: at 175% on a
       1920 screen the page gets 1097 CSS px, and a hard 350px crew panel was
       taking a third of it while the header ran 108px off the right edge.
       clamp() keeps the panel readable when there is room and lets it give
       ground when there is not. */
    --aside-w: clamp(248px, 22vw, 350px);
    --shell-pad: clamp(12px, 1.6vw, 24px);
    --header-min-h: 72px;
}

/* User text-size preference. Type that matters is expressed in rem below so
   this actually moves it, rather than only affecting stray unstyled text. */
html {
    font-size: calc(16px * var(--text-scale) / 100);
}

* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

::selection {
    background: var(--ember);
    color: #fff;
}

body {
    background-color: var(--bg-void);
    color: var(--text-main);
    font-family: 'Inter', sans-serif;
    line-height: 1.6;
    height: 100vh;
    overflow: hidden;
    position: relative;
    background-image:
        radial-gradient(circle at 12% 15%, rgba(156, 43, 78, 0.10) 0%, transparent 42%),
        radial-gradient(circle at 88% 12%, rgba(143, 199, 232, 0.08) 0%, transparent 40%),
        radial-gradient(circle at 50% 100%, rgba(255, 171, 61, 0.07) 0%, transparent 55%),
        linear-gradient(180deg, var(--bg-void) 0%, var(--bg-deep) 100%);
    background-attachment: fixed;
}

/* Subtle cinematic film-grain + vignette overlay, purely decorative */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 4000;
    background: radial-gradient(ellipse at center, transparent 55%, rgba(0, 0, 0, 0.55) 100%);
    mix-blend-mode: multiply;
}

body::after {
    content: "";
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 4001;
    opacity: 0.035;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    mix-blend-mode: overlay;
}

#app {
    display: grid;
    grid-template-columns: var(--aside-w) 1fr;
    /* `auto`, not a fixed 72px. The header is allowed to become two rows when
       the window narrows (see the header grid below); pinning the row height
       is what made the second row of gauges spill out over the arena at any
       browser zoom above ~150%. minmax(0,1fr) so the row below can actually
       shrink and its own scrollers take over. */
    grid-template-rows: auto minmax(0, 1fr);
    height: 100vh;
    height: 100dvh;
    width: 100%;
    position: relative;
    z-index: 2;
    /* `both`, not `opacity:0` + `forwards`. The old pairing made the game
       visible ONLY while this animation existed — so "Animation: None" and
       "Screen shake: off" (both of which null out animation-name) left the
       whole app stuck at opacity 0, i.e. a blank screen. */
    animation: appReveal 0.7s var(--ease-smooth) 0.15s both;
}

@keyframes appReveal {
    from { opacity: 0; transform: scale(0.99); }
    to   { opacity: 1; transform: scale(1); }
}

/* --- The masthead ---------------------------------------------------------
   Grid, not `flex + space-between`. Four rigid children on one flex line has
   an intrinsic width of ~1200px, so anything narrower — a 1366 laptop at 125%
   zoom, a 1080 screen at 175% — pushed the sound button, the settings gear and
   the version stamp clean off the right edge, and `.gauges` wrapped its second
   readout down past a header that was pinned to 72px. Named areas let the same
   four children re-flow into two and then three rows without hiding anything
   the player might want to read. */
header {
    grid-column: 1 / span 2;
    display: grid;
    align-items: center;
    column-gap: clamp(10px, 1.4vw, 22px);
    row-gap: 8px;
    /* Two bands, at every desktop width, on purpose.

       The survival strip (day, sea, stores, rigs, hunger/thirst) has a floor of
       ~530px and the wordmark + gauges + counters have a floor of ~880px. There
       is no viewport short of 1600px where all of it fits on one line, so the
       old single row did not "fit" at 1536 either — it silently wrapped the
       strip in place and grew to 130px, and where it grew moved every time the
       player touched the zoom. Giving the strip its own band costs ~40px on a
       big screen and buys a masthead that looks identical at 100%, 125%, 150%
       and 175%, which is the whole complaint. */
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas:
        "brand  gauges stats"
        "hud    hud    hud";
    min-height: var(--header-min-h);
    padding: 8px var(--shell-pad);
    background: rgba(10, 9, 16, 0.82);
    backdrop-filter: blur(14px);
    border-bottom: 1px solid rgba(255, 255, 255, 0.07);
    box-shadow: 0 4px 24px rgba(0, 0, 0, 0.35);
    z-index: 100;
    position: relative;
}

header::after {
    content: "";
    position: absolute;
    left: 0; right: 0; bottom: -1px;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--ember), var(--wine), var(--frost), transparent);
    opacity: 0.6;
}

h1 { grid-area: brand; }
.gauges { grid-area: gauges; }
.voyage-hud { grid-area: hud; }
.stats-bar { grid-area: stats; justify-self: end; }

/* Grid children default to min-width:auto, which refuses to shrink below the
   widest word inside. Without this the areas above are advisory only. */
header > * { min-width: 0; }

h1 {
    font-family: 'Cinzel', serif;
    font-size: clamp(15px, 1.15vw, 19px);
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    /* Static gradient. The old version shimmered on a 10s infinite loop, which
       read as a screensaver rather than a masthead. */
    background: linear-gradient(100deg, var(--amber-soft), var(--ember) 45%, var(--wine));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    cursor: pointer;
    white-space: nowrap;
    transition: filter 0.25s ease;
    user-select: none;
}

h1:hover {
    filter: brightness(1.18) drop-shadow(0 0 12px rgba(232, 112, 58, 0.4));
}

.stats-bar {
    display: flex;
    align-items: center;
    gap: clamp(8px, 0.8vw, 14px);
    font-family: 'Outfit', sans-serif;
    min-width: 0;
}

/* Instrument cluster: one housing, hairline-divided cells — instead of five
   free-floating "Label: 0" pills. */
.stat-cluster {
    display: flex;
    align-items: stretch;
    border: 1px solid rgba(255, 255, 255, 0.09);
    border-radius: 12px;
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.02));
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
    overflow: hidden;
}

.stat-pill {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1px;
    min-width: clamp(46px, 4.2vw, 68px);
    padding: 6px clamp(7px, 0.9vw, 14px);
    transition: background 0.25s ease;
}

.stat-pill + .stat-pill {
    border-left: 1px solid rgba(255, 255, 255, 0.07);
}

.stat-pill:hover {
    background: rgba(255, 255, 255, 0.05);
}

.stat-key {
    font-size: clamp(8px, 0.55vw, 9px);
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--text-dim);
}

.stat-val {
    font-size: clamp(13px, 0.9vw, 15px);
    font-weight: 700;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
    color: var(--text-main);
}

.header-tools {
    display: flex;
    gap: 8px;
}

/* ==========================================================================
   Sidebar Panel
   ========================================================================== */
aside {
    background: rgba(10, 9, 16, 0.55);
    border-right: 1px solid rgba(255, 255, 255, 0.05);
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 18px;
    overflow-y: auto;
    backdrop-filter: blur(14px);
    position: relative;
}

/* --- Collapsing the crew panel --------------------------------------------
   The column width lives on #app's grid, so collapsing is one custom property
   rather than a transform — no reflow surprises, and the arena genuinely gets
   the space back instead of having a hidden panel sitting under it. */
#app { grid-template-columns: var(--aside-w) 1fr; }
body.aside-collapsed { --aside-w: 0px; }

body.aside-collapsed aside {
    padding: 0;
    border-right-color: transparent;
    overflow: visible;
}

/* Everything except the handle goes away. The handle stays reachable, parked
   just outside the collapsed column. */
body.aside-collapsed aside > *:not(.aside-toggle) { display: none; }

.aside-toggle {
    position: absolute;
    top: 14px;
    right: -1px;
    z-index: 6;
    width: 22px;
    height: 34px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-right: none;
    border-radius: 8px 0 0 8px;
    background: rgba(10, 9, 16, 0.9);
    color: var(--text-muted);
    cursor: pointer;
    transition: color 0.2s ease, background 0.2s ease;
}

.aside-toggle:hover { color: var(--text-main); background: rgba(30, 26, 44, 0.95); }
.aside-toggle:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

.aside-toggle-glyph {
    font-size: 15px;
    line-height: 1;
    transition: transform 0.25s var(--ease-smooth);
}

body.aside-collapsed .aside-toggle {
    right: -23px;
    border-right: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 0 8px 8px 0;
}

body.aside-collapsed .aside-toggle-glyph { transform: rotate(180deg); }

.aside-title {
    font-family: 'Outfit', sans-serif;
    font-size: 13px;
    text-transform: uppercase;
    letter-spacing: 2px;
    color: var(--text-muted);
    border-bottom: 1px solid rgba(255, 255, 255, 0.07);
    padding-bottom: 10px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.aside-title::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--ember);
    box-shadow: 0 0 8px var(--ember);
    animation: dotPulse 1.8s ease-in-out infinite;
}

@keyframes dotPulse {
    0%, 100% { opacity: 0.4; transform: scale(0.8); }
    50% { opacity: 1; transform: scale(1.15); }
}

#sidebarPlayers {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.sidebar-player-card {
    background: var(--card-bg);
    border-radius: var(--radius-md);
    padding: 14px;
    border: 1px solid rgba(255, 255, 255, 0.04);
    box-shadow: var(--shadow-glow);
    transition: transform 0.25s var(--ease-pop), box-shadow 0.25s ease, border-color 0.25s ease;
    position: relative;
    overflow: hidden;
    opacity: 0;
    transform: translateX(-8px);
    animation: cardSlideIn 0.45s var(--ease-pop) forwards;
    animation-delay: calc(var(--i, 0) * 0.06s);
}

@keyframes cardSlideIn {
    to { opacity: 1; transform: translateX(0); }
}

.sidebar-player-card:hover {
    transform: translateY(-3px);
    box-shadow: 0 8px 28px rgba(156, 43, 78, 0.3);
    border-color: rgba(255, 255, 255, 0.1);
}

.player-name-row {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 10px;
}

.player-avatar {
    flex-shrink: 0;
    width: 46px;
    height: 46px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: 'Outfit', sans-serif;
    font-weight: 800;
    font-size: 15px;
    color: #060509;
    background: var(--avatar-color, #fff);
    box-shadow: 0 0 12px var(--avatar-color, rgba(255, 255, 255, 0.4));
    text-shadow: none;
    overflow: hidden;
    border: 2px solid var(--avatar-color, rgba(255, 255, 255, 0.6));
}

.avatar-face {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center top;
    border-radius: 50%;
}

.player-name-col {
    display: flex;
    flex-direction: column;
    line-height: 1.25;
    flex: 1;
    min-width: 0;
}

.player-name-lbl {
    font-family: 'Outfit', sans-serif;
    font-size: 15.5px;
    font-weight: 700;
}

.player-relation-lbl {
    font-size: 11px;
    color: var(--text-muted);
}

.player-stat-bar-row {
    margin-top: 8px;
}

.bar-lbl {
    display: flex;
    justify-content: space-between;
    font-size: 11px;
    color: var(--text-muted);
    margin-bottom: 3px;
}

.bar-lbl span {
    font-weight: 700;
    color: var(--text-main);
}

.bar-outer {
    background: rgba(255, 255, 255, 0.06);
    height: 7px;
    border-radius: 4px;
    overflow: hidden;
    position: relative;
}

.bar-inner {
    height: 100%;
    border-radius: 4px;
    transition: width 0.5s var(--ease-smooth);
    position: relative;
    overflow: hidden;
}

.bar-inner::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.45), transparent);
    width: 40%;
    animation: barShimmer 2.4s linear infinite;
}

@keyframes barShimmer {
    0% { transform: translateX(-120%); }
    100% { transform: translateX(320%); }
}

.corr-bar {
    background: linear-gradient(90deg, var(--ember), var(--wine));
}

.rel-bar {
    background: linear-gradient(90deg, var(--frost), var(--wine));
}

.status-badge {
    display: inline-block;
    font-size: 10px;
    font-weight: 700;
    padding: 3px 9px;
    border-radius: 5px;
    margin-top: 8px;
    letter-spacing: 0.3px;
}

.pregnant-badge {
    background: rgba(232, 112, 58, 0.15);
    color: var(--ember);
    border: 1px solid rgba(232, 112, 58, 0.3);
    animation: badgeGlow 2s ease-in-out infinite;
}

@keyframes badgeGlow {
    0%, 100% { box-shadow: 0 0 0 rgba(232, 112, 58, 0); }
    50% { box-shadow: 0 0 10px rgba(232, 112, 58, 0.45); }
}

.badge {
    font-size: 11px;
    font-weight: bold;
    padding: 3px 10px;
    border-radius: 12px;
}

.rival-badge {
    background: rgba(255, 51, 51, 0.15);
    color: #ff3333;
    border: 1px solid rgba(255, 51, 51, 0.3);
}

.neutral-badge {
    background: rgba(255, 255, 255, 0.05);
    color: var(--text-muted);
}

.sidebar-controls {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    margin-top: auto;
    padding-top: 6px;
}

/* ==========================================================================
   Main Arena Content Layout
   ========================================================================== */
main {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    position: relative;
}

.tabs-navigation {
    display: flex;
    background: rgba(10, 9, 16, 0.35);
    border-bottom: 1px solid rgba(255, 255, 255, 0.05);
    padding: 0 clamp(10px, 1.5vw, 20px);
    gap: clamp(2px, 0.6vw, 8px);
    min-height: 50px;
    align-items: flex-end;
    position: relative;
    /* Four tabs plus a hidden fifth run past 420px, and the one that fell off
       the end was Cheats — the tab this pass exists to improve. Scroll it
       instead of clipping it. The slider underneath is positioned against this
       element, so it travels with the scroll for free. */
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    scroll-behavior: smooth;
}

.tabs-navigation::-webkit-scrollbar { display: none; }
.tab-btn { flex: 0 0 auto; }

.tab-btn {
    background: none;
    border: none;
    color: var(--text-muted);
    font-family: 'Outfit', sans-serif;
    font-size: clamp(12px, 0.95vw, 14px);
    font-weight: 600;
    padding: 12px clamp(9px, 1.4vw, 20px);
    white-space: nowrap;
    cursor: pointer;
    border-bottom: 3px solid transparent;
    transition: color 0.2s ease;
    position: relative;
}

.tab-btn:hover {
    color: var(--text-main);
}

.tab-btn.active {
    color: var(--ember);
}

.tab-slider {
    position: absolute;
    bottom: -1px;
    height: 3px;
    border-radius: 3px 3px 0 0;
    background: linear-gradient(90deg, var(--ember), var(--wine));
    box-shadow: 0 0 10px rgba(232, 112, 58, 0.6);
    transition: left 0.32s var(--ease-pop), width 0.32s var(--ease-pop);
    pointer-events: none;
}

.panel-content {
    flex: 1;
    overflow-y: auto;
    padding: 24px;
    animation: panelFadeIn 0.35s var(--ease-smooth);
}

@keyframes panelFadeIn {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* ==========================================================================
   Gameplay Panel Elements
   ========================================================================== */
/*
 * The arena gets the whole width.
 *
 * This used to be `320px 1fr` sitting inside a `350px 1fr` app grid, so 670px —
 * over half of a 1280px screen — was permanently spent on a decorative bowl and
 * a crew panel, and the footage everybody actually came for was squeezed into
 * what was left. A reviewer put it exactly right: hiding the draw column makes
 * the layout better because it lets the video be bigger.
 *
 * The bowl is not deleted, though. It is the game's signature image and the
 * draw is its one moment of ceremony — it just stops paying rent between rounds.
 * See .draw-section below: it now floats over the arena and only while drawing.
 */
.play-layout {
    display: block;
    /* min-height, not height: the arena has its own min-height and grows with
       the scene, and a fixed 100% left this box shorter than its own content —
       which meant the bowl overlay (absolutely positioned against it) stopped
       short of the bottom of the arena it is supposed to cover. */
    min-height: 100%;
    position: relative;
}

/* ==========================================================================
   The Bowl of Fate — ticket draw mechanic (replaces the fortune wheel)
   ========================================================================== */
/*
 * The bowl, as an overlay.
 *
 * Present only while a draw is resolving (body.is-drawing, set in drawFlow).
 * pointer-events stay off so it can never swallow a click meant for the arena
 * underneath, and the whole thing is aria-hidden between draws.
 */
.draw-section {
    position: absolute;
    inset: 0;
    z-index: 5;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 16px;
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
    /* Only opacity interpolates. `visibility` flips discretely, delayed until
       the fade finishes on the way out and immediate on the way in.
       Transitioning visibility itself leaves it stuck half-applied whenever the
       browser suspends rendering (a background tab, a throttled window) — the
       overlay then sits over the arena until something forces a repaint. */
    transition: opacity 0.28s var(--ease-smooth), visibility 0s linear 0.28s;
    background: radial-gradient(ellipse at center, rgba(8, 7, 14, 0.82) 0%, rgba(8, 7, 14, 0.55) 55%, transparent 78%);
}

body.is-drawing .draw-section {
    opacity: 1;
    visibility: visible;
    transition: opacity 0.28s var(--ease-smooth), visibility 0s linear 0s;
}

/* Motion-off users get the bowl without the dissolve. */
html[data-motion="none"] .draw-section { transition: none; }

#drawStage {
    position: relative;
    width: 260px;
    height: 260px;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    perspective: 900px;
}

/* --- Fate bowl --- */
#fateBowl {
    position: relative;
    width: 210px;
    height: 150px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    transform-origin: 50% 90%;
}

#fateBowl.rattling {
    animation: bowlRattle 0.6s ease-in-out;
}

@keyframes bowlRattle {
    0%, 100% { transform: rotate(0) translateX(0); }
    15% { transform: rotate(-3deg) translateX(-4px); }
    30% { transform: rotate(3deg) translateX(4px); }
    45% { transform: rotate(-2.4deg) translateX(-3px); }
    60% { transform: rotate(2deg) translateX(3px); }
    80% { transform: rotate(-1deg) translateX(-1px); }
}

.bowl-halo {
    position: absolute;
    bottom: 6px;
    width: 200px;
    height: 110px;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(230, 192, 101, 0.2), transparent 66%);
    filter: blur(16px);
    z-index: 0;
    animation: haloPulse 4s ease-in-out infinite;
}

@keyframes haloPulse {
    0%, 100% { opacity: 0.5; transform: scale(1); }
    50% { opacity: 0.9; transform: scale(1.08); }
}

.bowl-body {
    position: relative;
    width: 190px;
    height: 112px;
    z-index: 2;
    border-radius: 0 0 95px 95px / 0 0 78px 78px;
    background:
        radial-gradient(ellipse at 50% -30%, rgba(230, 192, 101, 0.10), transparent 55%),
        linear-gradient(180deg, rgba(24, 28, 44, 0.75), rgba(10, 12, 22, 0.9) 75%);
    border: 1px solid rgba(230, 192, 101, 0.35);
    border-top: 2.5px solid var(--gold);
    box-shadow:
        inset 0 -22px 36px rgba(0, 0, 0, 0.55),
        inset 0 6px 18px rgba(230, 192, 101, 0.08),
        0 16px 34px rgba(0, 0, 0, 0.5),
        0 0 24px rgba(230, 192, 101, 0.12);
    overflow: hidden;
}

.bowl-glass {
    position: absolute;
    inset: 0;
    background: linear-gradient(115deg, rgba(255, 255, 255, 0.07) 4%, transparent 34%);
    pointer-events: none;
}

/* slim curved highlight along the inner-left wall (no more grey blob) */
.bowl-shine {
    position: absolute;
    top: 10px;
    left: 14px;
    width: 8px;
    height: 62px;
    border-radius: 50%;
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.28), transparent 85%);
    filter: blur(2px);
    transform: rotate(20deg);
    pointer-events: none;
}

.bowl-base {
    width: 74px;
    height: 12px;
    margin-top: -2px;
    z-index: 1;
    border-radius: 0 0 8px 8px;
    background: linear-gradient(180deg, rgba(230, 192, 101, 0.5), rgba(60, 46, 18, 0.9));
    box-shadow: 0 6px 14px rgba(0, 0, 0, 0.5);
}

.bowl-caption {
    margin-top: 12px;
    font-family: 'Cinzel', serif;
    font-size: 12px;
    letter-spacing: 3px;
    text-transform: uppercase;
    color: var(--gold);
    opacity: 0.85;
    text-shadow: 0 0 10px rgba(230, 192, 101, 0.45);
}

/* Folded tickets bobbing inside the bowl */
.bowl-tickets {
    position: absolute;
    inset: 0;
    z-index: 3;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}

.folded-ticket {
    position: absolute;
    bottom: 18px;
    left: calc(50% + var(--tx, 0));
    width: 24px;
    height: 16px;
    border-radius: 3px;
    background:
        linear-gradient(180deg, #f7ecd6, #d9c39a);
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.4), inset 0 0 0 1px rgba(120, 90, 50, 0.25);
    transform: translateX(-50%) rotate(var(--r, 0));
    animation: ticketBob 3.2s ease-in-out infinite;
    animation-delay: var(--d, 0s);
}

.folded-ticket::after {
    content: "";
    position: absolute;
    top: 0; left: 50%;
    width: 1px; height: 100%;
    background: rgba(120, 90, 50, 0.35);
}

#fateBowl.rattling .folded-ticket {
    animation: ticketShuffle 0.6s ease-in-out;
}

@keyframes ticketBob {
    0%, 100% { transform: translateX(-50%) rotate(var(--r, 0)) translateY(0); }
    50% { transform: translateX(-50%) rotate(var(--r, 0)) translateY(-4px); }
}

@keyframes ticketShuffle {
    0%, 100% { transform: translateX(-50%) rotate(var(--r, 0)); }
    25% { transform: translateX(calc(-50% - 6px)) rotate(calc(var(--r) + 20deg)) translateY(-6px); }
    50% { transform: translateX(calc(-50% + 8px)) rotate(calc(var(--r) - 16deg)) translateY(-3px); }
    75% { transform: translateX(calc(-50% - 3px)) rotate(calc(var(--r) + 8deg)); }
}

/* --- The reveal ticket (flies out & unfolds) --- */
.reveal-ticket {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 20;
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
    perspective: 1000px;
}

.reveal-ticket.active {
    visibility: visible;
    opacity: 1;
}

.rt-card {
    position: relative;
    width: 210px;
    height: 130px;
    transform-style: preserve-3d;
    transform: translateY(70px) scale(0.25) rotate(-14deg);
    opacity: 0;
}

.reveal-ticket.emerging .rt-card {
    animation: ticketEmerge 0.62s cubic-bezier(.2,.8,.25,1.2) forwards;
}

@keyframes ticketEmerge {
    0%   { transform: translateY(70px) scale(0.25) rotate(-16deg); opacity: 0; }
    55%  { opacity: 1; }
    100% { transform: translateY(0) scale(1) rotate(0deg); opacity: 1; }
}

.reveal-ticket.settled .rt-card {
    transform: translateY(0) scale(1) rotate(0deg);
    opacity: 1;
}

.reveal-ticket.dismiss .rt-card {
    animation: ticketDismiss 0.42s ease-in forwards;
}

@keyframes ticketDismiss {
    0%   { transform: translateY(0) scale(1) rotate(0); opacity: 1; }
    100% { transform: translateY(-60px) scale(0.7) rotate(8deg); opacity: 0; }
}

/* The revealed body under the flaps */
.rt-body {
    position: absolute;
    inset: 0;
    border-radius: 12px;
    background:
        radial-gradient(circle at 50% 0%, rgba(255, 255, 255, 0.08), transparent 60%),
        linear-gradient(155deg, #1a1526, #241a2e 55%, #120e1c);
    border: 1px solid var(--rt-color, var(--ember));
    box-shadow: 0 18px 40px rgba(0, 0, 0, 0.6), inset 0 0 26px rgba(0,0,0,0.5), 0 0 26px var(--rt-color, var(--ember));
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    overflow: hidden;
}

.rt-body::before {
    content: "";
    position: absolute;
    inset: 5px;
    border-radius: 8px;
    border: 1px dashed rgba(255, 255, 255, 0.14);
    pointer-events: none;
}

.rt-role {
    font-family: 'Cinzel', serif;
    font-size: 10px;
    letter-spacing: 3px;
    text-transform: uppercase;
    color: var(--text-muted);
    opacity: 0;
    transform: translateY(6px);
}

.rt-name {
    font-family: 'Outfit', sans-serif;
    font-size: 30px;
    font-weight: 800;
    letter-spacing: 0.5px;
    color: var(--rt-color, #fff);
    text-shadow: 0 0 18px var(--rt-color, rgba(255,255,255,0.4));
    opacity: 0;
    transform: scale(0.7);
}

.rt-relation {
    font-size: 11px;
    color: var(--text-muted);
    opacity: 0;
    transform: translateY(6px);
}

.reveal-ticket.open .rt-role { animation: rtTextIn 0.4s ease 0.18s forwards; }
.reveal-ticket.open .rt-name { animation: rtNameIn 0.5s var(--ease-pop) 0.24s forwards; }
.reveal-ticket.open .rt-relation { animation: rtTextIn 0.4s ease 0.34s forwards; }

@keyframes rtTextIn {
    to { opacity: 1; transform: translateY(0); }
}
@keyframes rtNameIn {
    to { opacity: 1; transform: scale(1); }
}

/* Wax seal stamp */
.rt-seal {
    position: absolute;
    bottom: 8px;
    right: 10px;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: radial-gradient(circle at 35% 30%, var(--rt-color, var(--wine)), #4a0d20 80%);
    color: rgba(255, 255, 255, 0.85);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.6);
    opacity: 0;
    transform: scale(2.4) rotate(-20deg);
}

.reveal-ticket.sealed .rt-seal {
    animation: sealStamp 0.4s var(--ease-pop) forwards;
}

@keyframes sealStamp {
    0%   { opacity: 0; transform: scale(2.4) rotate(-20deg); }
    60%  { opacity: 1; transform: scale(0.86) rotate(4deg); }
    100% { opacity: 1; transform: scale(1) rotate(0); }
}

/* The two paper flaps covering the ticket while folded */
.rt-flap {
    position: absolute;
    left: 0;
    width: 100%;
    height: 50%;
    z-index: 5;
    background:
        repeating-linear-gradient(180deg, rgba(0,0,0,0.03) 0 3px, transparent 3px 6px),
        linear-gradient(180deg, #f4e8cf, #dcc59a);
    backface-visibility: hidden;
    box-shadow: inset 0 0 0 1px rgba(120, 90, 50, 0.3);
}

.rt-flap-top {
    top: 0;
    transform-origin: bottom center;
    border-radius: 12px 12px 2px 2px;
    box-shadow: inset 0 0 0 1px rgba(120, 90, 50, 0.3), 0 2px 4px rgba(0,0,0,0.3);
}

.rt-flap-bottom {
    bottom: 0;
    transform-origin: top center;
    border-radius: 2px 2px 12px 12px;
}

/* crease line down the fold */
.rt-flap-top::after,
.rt-flap-bottom::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 0;
    width: 1px;
    height: 100%;
    background: rgba(120, 90, 50, 0.28);
}

.reveal-ticket.open .rt-flap-top {
    transform: rotateX(168deg);
    transition: transform 0.55s cubic-bezier(.4,0,.2,1);
    box-shadow: 0 -10px 20px rgba(0,0,0,0.4);
}

.reveal-ticket.open .rt-flap-bottom {
    transform: rotateX(-168deg);
    transition: transform 0.55s cubic-bezier(.4,0,.2,1);
    box-shadow: 0 10px 20px rgba(0,0,0,0.4);
}

.arena-section {
    display: flex;
    flex-direction: column;
    gap: 20px;
    background: rgba(20, 18, 32, 0.35);
    border-radius: var(--radius-lg);
    border: 1px solid rgba(255, 255, 255, 0.05);
    padding: 22px;
    min-height: 500px;
    box-shadow: 0 20px 50px rgba(0, 0, 0, 0.25);
    position: relative;
}

#roundTitle {
    font-family: 'Outfit', sans-serif;
    font-size: 19px;
    font-weight: 700;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
    padding-bottom: 12px;
    letter-spacing: 0.2px;
}

.glow-txt-cyan {
    color: var(--frost);
    text-shadow: 0 0 12px rgba(143, 199, 232, 0.7);
    animation: textPulse 1s ease-in-out infinite;
}

.glow-txt-pink {
    color: var(--ember);
    text-shadow: 0 0 12px rgba(232, 112, 58, 0.7);
    animation: textPulse 1s ease-in-out infinite;
}

@keyframes textPulse {
    0%, 100% { opacity: 0.75; }
    50% { opacity: 1; }
}

.arena-body {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

/* Action Box Group styling */
.action-box-group {
    background: rgba(255, 255, 255, 0.02);
    border-radius: 12px;
    padding: 14px;
    border: 1px solid rgba(255, 255, 255, 0.05);
    animation: panelFadeIn 0.3s var(--ease-smooth);
}

.action-box-title {
    font-family: 'Outfit', sans-serif;
    font-size: 12.5px;
    text-transform: uppercase;
    letter-spacing: 1.2px;
    color: var(--frost);
    margin-bottom: 10px;
    font-weight: bold;
}

.action-box-flex {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

/* Speech bubbles and narrative formatting */
.speech-bubble {
    background: rgba(255, 255, 255, 0.035);
    border-radius: 12px;
    padding: 12px 16px;
    animation: bubbleFadeIn 0.35s var(--ease-pop) forwards;
    position: relative;
}

.bubble-name {
    font-family: 'Outfit', sans-serif;
    font-weight: 700;
    font-size: 13px;
    margin-bottom: 4px;
}

.bubble-text {
    font-size: 14px;
    font-style: italic;
    color: #dedbe6;
}

.narrative-line {
    font-size: 13.5px;
    color: var(--text-muted);
    padding: 5px 10px;
    border-left: 2px solid rgba(255, 255, 255, 0.1);
    animation: panelFadeIn 0.3s var(--ease-smooth);
}

/* The scene's own narration — what the round actually looked like, as opposed
   to the status lines around it. It has no name on it on purpose: these lines
   are written in the third person about whoever is performing, so putting a
   speaker's name in front of them is what made rounds read as though the wrong
   person was talking. Brighter than a status line because this IS the story. */
.scene-prose {
    color: var(--text-main);
    border-left-color: rgba(255, 255, 255, 0.22);
}

.auto-thinking {
    color: var(--amber-soft);
    font-style: italic;
    position: relative;
}

.auto-thinking::after {
    content: "";
    display: inline-block;
    width: 4px;
    height: 4px;
    margin-left: 6px;
    border-radius: 50%;
    background: var(--amber-soft);
    box-shadow: 8px 0 0 0 var(--amber-soft), 16px 0 0 0 var(--amber-soft);
    animation: thinkingDots 1.1s ease-in-out infinite;
}

@keyframes thinkingDots {
    0%, 100% { opacity: 0.25; }
    50% { opacity: 1; }
}

.text-speech-line {
    font-size: 13.5px;
    padding: 8px 4px;
}

.visual-image-wrapper {
    max-width: 100%;
    margin: 12px auto;
    border-radius: 14px;
    overflow: hidden;
    border: 1px solid rgba(255, 255, 255, 0.08);
    box-shadow: 0 14px 40px rgba(0, 0, 0, 0.55);
    animation: imagePop 0.45s var(--ease-pop) forwards;
    position: relative;
}

.visual-image-wrapper::after {
    content: "";
    position: absolute;
    inset: 0;
    box-shadow: inset 0 -40px 40px -20px rgba(0, 0, 0, 0.55);
    pointer-events: none;
}

.scene-visual-img {
    display: block;
    max-width: 100%;
    max-height: 400px;
    object-fit: contain;
    margin: 0 auto;
}

/* The face of whoever answers a social beat that has no footage (Media.faceBeatFor).
   A portrait card, deliberately smaller than a scene clip: it gives the beat a
   face without pretending to be one. The stills are square. */
.visual-face-wrapper {
    width: 220px;
    max-width: 60%;
    border-radius: 18px;
}

.scene-visual-face {
    display: block;
    width: 100%;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    object-position: center top;
}

/* ==========================================================================
   Buttons and Inputs
   ========================================================================== */
.nav-btn, .action-btn, .gallery-btn {
    font-family: 'Outfit', sans-serif;
    font-weight: bold;
    cursor: pointer;
    border: none;
    border-radius: var(--radius-sm);
    transition: all 0.2s var(--ease-smooth);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    position: relative;
    overflow: hidden;
}

.nav-btn {
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    color: var(--text-main);
    padding: 9px 16px;
    font-size: 13px;
}

.nav-btn:hover:not(:disabled) {
    background: rgba(255, 255, 255, 0.1);
    border-color: rgba(255, 255, 255, 0.2);
    transform: translateY(-1px);
}

.nav-btn:active:not(:disabled), .action-btn:active:not(:disabled), .btn-harem:active:not(:disabled), .btn-marry:active:not(:disabled) {
    transform: scale(0.96) !important;
}

.btn-marry {
    background: linear-gradient(135deg, var(--gold), #b5852f);
    color: #241a08;
    border: none;
}

.btn-marry:hover:not(:disabled) {
    box-shadow: 0 0 16px rgba(230, 192, 101, 0.5);
    transform: scale(1.03);
}

.btn-harem {
    background: linear-gradient(135deg, var(--ember), var(--wine));
    color: #ffffff;
    box-shadow: var(--shadow-btn);
}

.btn-harem:hover:not(:disabled) {
    box-shadow: 0 0 28px rgba(232, 112, 58, 0.65);
    transform: scale(1.03);
}

.btn-harem:disabled, .nav-btn:disabled, .action-btn:disabled {
    opacity: 0.45;
    cursor: not-allowed;
    transform: none !important;
}

.action-btn {
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid rgba(255, 255, 255, 0.06);
    color: var(--frost);
    padding: 10px 14px;
    font-size: 13px;
    border-radius: 8px;
}

.action-btn:hover:not(:disabled) {
    background: rgba(143, 199, 232, 0.1);
    border-color: var(--frost);
    color: #ffffff;
    transform: translateY(-2px);
    box-shadow: 0 6px 18px rgba(143, 199, 232, 0.2);
}

.req-badge {
    background: rgba(255, 255, 255, 0.07);
    padding: 2px 6px;
    border-radius: 4px;
    font-size: 10px;
    color: var(--text-muted);
    margin-left: 6px;
}

.btn-danger-glow {
    color: var(--ember) !important;
    border-color: rgba(232, 112, 58, 0.3) !important;
}

.btn-danger-glow:hover:not(:disabled) {
    background: rgba(232, 112, 58, 0.15) !important;
    border-color: var(--ember) !important;
    color: #fff !important;
}

.finish-choice-box {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    background: rgba(232, 112, 58, 0.04);
    border: 1px solid rgba(232, 112, 58, 0.18);
    border-radius: var(--radius-md);
    padding: 16px;
    animation: panelFadeIn 0.3s var(--ease-smooth);
}

.scene-hold-indicator {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    padding: 14px 16px;
    border-radius: var(--radius-md);
    border: 1px solid rgba(255, 255, 255, 0.06);
    background: rgba(255, 255, 255, 0.02);
    animation: panelFadeIn 0.3s var(--ease-smooth);
}

.finish-choice-box h3 {
    font-family: 'Outfit', sans-serif;
    font-size: 14px;
    width: 100%;
    margin-bottom: 4px;
    color: var(--ember-soft);
}

/* ==========================================================================
   Modals
   ========================================================================== */
.modal-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    background: rgba(4, 3, 8, 0.82);
    backdrop-filter: blur(16px);
    z-index: 1000;
    display: none; /* Flex when active */
    justify-content: center;
    align-items: center;
    padding: 20px;
    animation: overlayFade 0.3s var(--ease-smooth);
}

@keyframes overlayFade {
    from { background: rgba(4, 3, 8, 0); backdrop-filter: blur(0); }
    to   { background: rgba(4, 3, 8, 0.82); backdrop-filter: blur(16px); }
}

.modal-card {
    background: var(--card-bg-solid);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: var(--radius-lg);
    max-width: 650px;
    width: 100%;
    max-height: 90vh;
    overflow-y: auto;
    padding: 32px;
    box-shadow: 0 30px 70px rgba(156, 43, 78, 0.25), 0 0 0 1px rgba(255, 255, 255, 0.02);
    text-align: center;
    animation: modalSlideUp 0.4s var(--ease-pop) forwards;
    position: relative;
}

.modal-card::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: var(--radius-lg);
    padding: 1px;
    background: linear-gradient(135deg, var(--ember), transparent 30%, transparent 70%, var(--frost));
    -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
    -webkit-mask-composite: xor;
    mask-composite: exclude;
    opacity: 0.5;
    pointer-events: none;
}

.modal-card h2 {
    font-family: 'Outfit', sans-serif;
    font-size: 23px;
    margin-bottom: 16px;
    letter-spacing: 0.2px;
}

.modal-media {
    display: flex;
    justify-content: center;
}

.modal-img {
    max-width: 100%;
    max-height: 300px;
    border-radius: 14px;
    margin: 16px 0;
    box-shadow: 0 16px 34px rgba(0, 0, 0, 0.5);
}

.modal-desc {
    color: var(--text-muted);
    font-size: 15px;
    margin-bottom: 26px;
}

@keyframes modalSlideUp {
    0% {
        opacity: 0;
        transform: translateY(30px) scale(0.97);
    }
    100% {
        opacity: 1;
        transform: translateY(0) scale(1);
    }
}

/* Confetti burst for celebratory endings */
#confettiLayer {
    position: fixed;
    inset: 0;
    z-index: 1100;
    pointer-events: none;
    overflow: hidden;
}

.confetti-piece {
    position: absolute;
    top: -5vh;
    left: var(--x, 50%);
    width: var(--w, 8px);
    height: var(--w, 8px);
    background: var(--c, var(--ember));
    border-radius: 2px;
    opacity: 0.9;
    animation: confettiFall var(--dur, 3s) ease-in forwards;
    animation-delay: var(--delay, 0s);
}

@keyframes confettiFall {
    0% { transform: translate3d(0, -5vh, 0) rotate(0deg); opacity: 1; }
    100% { transform: translate3d(var(--drift, 80px), 105vh, 0) rotate(600deg); opacity: 0; }
}

/* Screen flash for dramatic beats (impregnation, etc.) */
#flashOverlay {
    position: fixed;
    inset: 0;
    z-index: 1050;
    pointer-events: none;
    background: radial-gradient(circle at 50% 50%, rgba(232, 112, 58, 0.35), transparent 70%);
    opacity: 0;
}

#flashOverlay.active {
    animation: flashPulse 0.9s ease-out;
}

@keyframes flashPulse {
    0% { opacity: 0; }
    15% { opacity: 1; }
    100% { opacity: 0; }
}

/* ==========================================================================
   Gallery Panel
   ========================================================================== */
.gallery-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 20px;
}

.gallery-card {
    background: var(--card-bg);
    border-radius: 16px;
    padding: 20px;
    border: 1px solid rgba(255, 255, 255, 0.04);
    box-shadow: var(--shadow-glow);
    display: flex;
    flex-direction: column;
    gap: 12px;
    transition: transform 0.25s var(--ease-pop), box-shadow 0.25s ease;
}

.gallery-card:hover {
    transform: translateY(-4px);
    box-shadow: 0 14px 34px rgba(156, 43, 78, 0.28);
}

.gallery-card h3 {
    font-family: 'Outfit', sans-serif;
    font-size: 18px;
    margin-bottom: 0;
}

.gallery-card-head {
    display: flex;
    align-items: center;
    gap: 12px;
}

.gallery-face {
    display: block;
    flex-shrink: 0;
    width: 56px;
    height: 56px;
    border-radius: 50%;
    object-fit: cover;
    object-position: center top;
    box-shadow: 0 0 14px rgba(255, 255, 255, 0.15);
    border: 2px solid rgba(255, 255, 255, 0.18);
}

.gallery-btn {
    padding: 12px;
    font-size: 13px;
    width: 100%;
}

.gallery-btn.unlocked {
    background: rgba(143, 199, 232, 0.1);
    border: 1px solid var(--frost);
    color: #ffffff;
}

.gallery-btn.unlocked:hover {
    background: var(--frost);
    color: #000;
    box-shadow: 0 0 18px rgba(143, 199, 232, 0.5);
}

.gallery-btn.locked {
    background: rgba(255, 255, 255, 0.02);
    border: 1px solid rgba(255, 255, 255, 0.05);
    color: var(--text-muted);
    cursor: not-allowed;
}

/* ==========================================================================
   Achievements Items
   ========================================================================== */
.achievement-item {
    display: grid;
    grid-template-columns: 2fr 1fr 1fr;
    align-items: center;
    padding: 11px 16px;
    background: rgba(255, 255, 255, 0.02);
    border-radius: 8px;
    border: 1px solid rgba(255, 255, 255, 0.05);
    margin-bottom: 8px;
    gap: 8px;
    transition: border-color 0.2s ease;
}

.achievement-item:hover {
    border-color: rgba(156, 43, 78, 0.3);
}

.ach-name {
    font-family: 'Outfit', sans-serif;
    font-weight: 700;
    text-align: left;
}

.ach-badge {
    font-size: 11px;
    font-weight: bold;
    padding: 4px 0;
    border-radius: 4px;
}

.ach-badge.unlocked {
    background: rgba(230, 192, 101, 0.15);
    color: var(--gold);
    border: 1px solid rgba(230, 192, 101, 0.3);
}

.ach-badge.locked {
    background: rgba(255, 255, 255, 0.03);
    color: var(--text-muted);
    border: 1px solid rgba(255, 255, 255, 0.05);
}

/* ==========================================================================
   Cheats Panel
   ========================================================================== */
.cheat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 20px;
}

.cheat-card {
    background: rgba(255, 51, 51, 0.02);
    border: 1px solid rgba(255, 51, 51, 0.1);
    border-radius: 16px;
    padding: 20px;
    box-shadow: 0 0 20px rgba(255, 51, 51, 0.05);
}

.cheat-card h3 {
    font-family: 'Outfit', sans-serif;
    font-size: 16px;
    color: #ff5555;
    margin-bottom: 12px;
}

.cheat-control {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.cheat-select, .cheat-input {
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 6px;
    padding: 8px;
    color: var(--text-main);
    font-family: inherit;
    font-size: 13px;
}

.cheat-btn {
    background: rgba(255, 51, 51, 0.1);
    border: 1px solid rgba(255, 51, 51, 0.3);
    color: #ffdddd;
    padding: 8px;
    border-radius: 6px;
    font-weight: bold;
    cursor: pointer;
    transition: all 0.2s;
}

.cheat-btn:hover {
    background: #ff3333;
    color: #fff;
    box-shadow: 0 0 10px rgba(255, 51, 51, 0.4);
}

/* ==========================================================================
   Persistent Logs Tab
   ========================================================================== */
.persistent-log-entry {
    font-family: 'JetBrains Mono', 'Courier New', monospace;
    font-size: 12.5px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.03);
    padding: 7px 4px;
}

.persistent-log-entry:hover {
    background: rgba(255, 255, 255, 0.02);
}

.log-timestamp {
    color: var(--frost);
}

/* ==========================================================================
   Animations
   ========================================================================== */
@keyframes bubbleFadeIn {
    0% {
        opacity: 0;
        transform: scale(0.95) translateY(10px);
    }
    100% {
        opacity: 1;
        transform: scale(1.0) translateY(0);
    }
}

@keyframes imagePop {
    0% {
        opacity: 0;
        transform: scale(0.97);
    }
    100% {
        opacity: 1;
        transform: scale(1.0);
    }
}

/* Floating Number Effect */
.floating-indicator {
    position: absolute;
    transform: translate(-50%, -50%);
    font-family: 'Outfit', sans-serif;
    font-weight: 800;
    font-size: 15px;
    padding: 8px 18px;
    border-radius: 100px;
    pointer-events: none;
    z-index: 10000;
    background: rgba(10, 9, 16, 0.85);
    border: 1px solid currentColor;
    backdrop-filter: blur(6px);
    animation: floatUp 1.4s var(--ease-pop) forwards;
    white-space: nowrap;
}

.floating-corruption {
    color: var(--ember);
    box-shadow: 0 0 18px rgba(232, 112, 58, 0.5);
    text-shadow: 0 0 8px rgba(232, 112, 58, 0.6);
}

.floating-relationship {
    color: var(--frost);
    box-shadow: 0 0 18px rgba(143, 199, 232, 0.5);
    text-shadow: 0 0 8px rgba(143, 199, 232, 0.6);
}

.floating-save {
    color: var(--gold);
    box-shadow: 0 0 18px rgba(230, 192, 101, 0.5);
    text-shadow: 0 0 8px rgba(230, 192, 101, 0.6);
}

@keyframes floatUp {
    0% {
        opacity: 0;
        top: 65%;
        transform: translate(-50%, -50%) scale(0.8);
    }
    15% {
        opacity: 1;
        transform: translate(-50%, -50%) scale(1.05);
    }
    100% {
        opacity: 0;
        top: 25%;
        transform: translate(-50%, -50%) scale(1);
    }
}

/* Banish pregnancy select cards */
.banish-abort-card {
    background: rgba(255, 255, 255, 0.03);
    padding: 14px;
    border-radius: 10px;
    margin-bottom: 12px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    border: 1px solid rgba(255, 255, 255, 0.05);
}

/* Custom Alert classes for logs */
.jealousy-alert { color: var(--ember); font-weight: bold; }
.refusal-alert { color: #ff5555; }
.pregnancy-alert { color: var(--ember); font-weight: 800; text-decoration: underline; }
.sober-alert { color: var(--gold); }
.success-log { color: var(--gold); }
.stat-change { color: #ffcc00; }
.save-log { color: var(--frost); }
.status-log { color: var(--amber-soft); font-weight: 600; }

.intro-banner {
    background: rgba(156, 43, 78, 0.05);
    border: 1px solid rgba(156, 43, 78, 0.2);
    border-radius: var(--radius-md);
    padding: 26px;
    margin: 20px 0;
    box-shadow: var(--shadow-glow);
    animation: panelFadeIn 0.4s var(--ease-smooth);
}

.intro-banner h2 {
    font-family: 'Outfit', sans-serif;
    margin-bottom: 12px;
    color: var(--ember);
}

.intro-banner p {
    margin-bottom: 12px;
    font-size: 14px;
}

/* Custom Scrollbars */
::-webkit-scrollbar {
    width: 6px;
    height: 6px;
}
::-webkit-scrollbar-track {
    background: rgba(0, 0, 0, 0.2);
}
::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.1);
    border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
    background: var(--ember);
}

/* Epilogue scroll window */
.epilogue-text-box {
    text-align: left;
    max-height: 250px;
    overflow-y: auto;
    background: rgba(0, 0, 0, 0.25);
    padding: 15px;
    border-radius: 8px;
    border: 1px solid rgba(255, 255, 255, 0.05);
    margin: 15px 0;
    font-size: 14px;
}
.epilogue-text-box h3 {
    margin-bottom: 8px;
    color: var(--ember);
}
.epilogue-text-box p {
    margin-bottom: 12px;
}

/* ==========================================================================
   Draw button + hint
   ========================================================================== */
.draw-btn {
    position: relative;
    overflow: hidden;
    font-size: 15px !important;
    padding: 13px 34px !important;
    letter-spacing: 1.5px;
    text-transform: uppercase;
}

.draw-btn::after {
    content: "";
    position: absolute;
    top: 0; left: -60%;
    width: 40%; height: 100%;
    background: linear-gradient(115deg, transparent, rgba(255, 255, 255, 0.4), transparent);
    animation: shineSweep 3.4s ease-in-out infinite;
    pointer-events: none;
}

@keyframes shineSweep {
    0%   { left: -60%; }
    35%  { left: 130%; }
    100% { left: 130%; }
}

.draw-btn.drawing {
    opacity: 0.6;
    pointer-events: none;
}

/* Now a full-width line under the arena header rather than a narrow caption
   stacked under the bowl, so it reads as one sentence instead of four. */
.draw-hint {
    font-size: 11.5px;
    color: var(--text-dim);
    letter-spacing: 0.4px;
    margin-top: -12px;
}

/* The arena's own header row: what this round is, and the button that starts
   the next one. The draw used to live in its own 320px column; putting it here
   keeps it exactly where the player is already looking. */
.arena-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 18px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
    padding-bottom: 12px;
}

.arena-head #roundTitle {
    border-bottom: none;
    padding-bottom: 0;
    flex: 1;
    min-width: 0;
}

.arena-head .draw-btn {
    flex: 0 0 auto;
    font-size: 13px !important;
    padding: 10px 22px !important;
}

/* ==========================================================================
   Tide phase indicator (header nautical mechanic)
   ========================================================================== */
.tide-indicator {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 14px 6px 10px;
    border-radius: 30px;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.08);
    transition: border-color 0.3s ease, box-shadow 0.3s ease;
}

.tide-indicator:hover {
    border-color: var(--tide-tint, var(--frost));
    box-shadow: 0 0 16px color-mix(in srgb, var(--tide-tint, var(--frost)) 30%, transparent);
}

.tide-glyph {
    font-size: 18px;
    filter: drop-shadow(0 0 6px var(--tide-tint, var(--frost)));
    animation: tideBob 3s ease-in-out infinite;
}

@keyframes tideBob {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-2px); }
}

.tide-info {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 130px;
}

.tide-phase-name {
    font-family: 'Cinzel', serif;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 1px;
    color: var(--tide-tint, var(--frost));
    text-shadow: 0 0 10px color-mix(in srgb, var(--tide-tint, var(--frost)) 45%, transparent);
    transition: color 0.5s ease;
}

.tide-track {
    height: 5px;
    border-radius: 4px;
    background: rgba(255, 255, 255, 0.08);
    overflow: hidden;
}

.tide-fill {
    height: 100%;
    width: 0%;
    border-radius: 4px;
    background: linear-gradient(90deg, var(--frost), var(--tide-tint, var(--wine)));
    box-shadow: 0 0 10px var(--tide-tint, var(--frost));
    transition: width 0.7s var(--ease-smooth), background 0.6s ease;
    position: relative;
    overflow: hidden;
}

.tide-fill::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,0.5), transparent);
    width: 40%;
    animation: barShimmer 2.6s linear infinite;
}

.tide-flash {
    animation: tideFlash 1s ease-out;
}

@keyframes tideFlash {
    0% { box-shadow: 0 0 0 rgba(255,255,255,0); }
    30% { box-shadow: 0 0 26px var(--tide-tint, var(--frost)); }
    100% { box-shadow: 0 0 0 rgba(255,255,255,0); }
}

/* ==========================================================================
   Splash badge
   ========================================================================== */
.splash-badge {
    font-family: 'Outfit', sans-serif;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: #fff;
    padding: 5px 16px;
    border-radius: 30px;
    background: linear-gradient(135deg, var(--wine), var(--ember));
    box-shadow: 0 0 20px rgba(232, 112, 58, 0.4);
    margin-bottom: 8px;
    animation: splashFadeIn 1s var(--ease-smooth) 0.3s both;
}

/* ==========================================================================
   Save / Load Slots Modal (Captain's Logbook)
   ========================================================================== */
.slots-card {
    max-width: 560px;
}

.slots-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin: 20px 0 24px;
    text-align: left;
}

.slot-row {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 14px 16px;
    border-radius: var(--radius-md);
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid rgba(255, 255, 255, 0.06);
    transition: transform 0.2s var(--ease-pop), border-color 0.2s ease, box-shadow 0.2s ease;
    opacity: 0;
    transform: translateY(8px);
    animation: cardSlideIn 0.4s var(--ease-pop) forwards;
    animation-delay: calc(var(--i, 0) * 0.06s);
}

.slot-row:hover {
    transform: translateY(-2px);
    border-color: rgba(143, 199, 232, 0.35);
    box-shadow: 0 8px 22px rgba(0, 0, 0, 0.35);
}

.slot-medallion {
    flex-shrink: 0;
    width: 46px;
    height: 46px;
    border-radius: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: 'Cinzel', serif;
    font-weight: 700;
    font-size: 20px;
    color: #fff;
    background: linear-gradient(135deg, var(--ember), var(--wine));
    box-shadow: 0 0 16px rgba(232, 112, 58, 0.35);
}

.slot-row.empty .slot-medallion {
    background: rgba(255, 255, 255, 0.05);
    color: var(--text-dim);
    box-shadow: none;
}

.slot-info {
    flex: 1;
    min-width: 0;
}

.slot-title {
    font-family: 'Outfit', sans-serif;
    font-weight: 700;
    font-size: 15px;
    color: var(--text-main);
}

.slot-meta {
    font-size: 11.5px;
    color: var(--text-muted);
    margin-top: 2px;
}

.slot-meta .slot-dot {
    color: var(--frost);
    margin: 0 5px;
}

.slot-timestamp {
    font-size: 10.5px;
    color: var(--text-dim);
    margin-top: 3px;
    font-family: 'JetBrains Mono', 'Courier New', monospace;
}

.slot-actions {
    display: flex;
    flex-direction: column;
    gap: 6px;
    flex-shrink: 0;
}

.slot-btn {
    font-family: 'Outfit', sans-serif;
    font-weight: 700;
    font-size: 11.5px;
    padding: 6px 14px;
    border-radius: 6px;
    cursor: pointer;
    border: 1px solid rgba(255, 255, 255, 0.12);
    background: rgba(255, 255, 255, 0.05);
    color: var(--text-main);
    transition: all 0.2s ease;
}

.slot-btn.primary {
    background: linear-gradient(135deg, var(--ember), var(--wine));
    border: none;
    color: #fff;
}

.slot-btn.primary:hover {
    box-shadow: 0 0 14px rgba(232, 112, 58, 0.5);
    transform: translateY(-1px);
}

.slot-btn.ghost:hover:not(:disabled) {
    border-color: var(--frost);
    color: #fff;
}

.slot-btn.danger {
    color: var(--danger);
    border-color: rgba(229, 73, 63, 0.3);
}

.slot-btn.danger:hover {
    background: rgba(229, 73, 63, 0.15);
    color: #fff;
}

.slot-btn:disabled {
    opacity: 0.35;
    cursor: not-allowed;
}

/* ==========================================================================
   Responsive Layout

   WHY THE LADDER IS THIS DENSE

   The only breakpoints that existed were 980px and 560px, and the shell's
   natural width is ~1200px. That left a 220px-wide dead zone — exactly where a
   1920 screen lands at 175% browser zoom, and a 1366 laptop at 125% — in which
   the header overflowed the viewport by 108px and the sound, settings and
   version controls sat off-screen with no way to scroll to them. Zoom is not
   an edge case here: it is how a lot of people read a text-heavy game.

   Each step below trades one thing away, in order of how little it costs:
   spacing first, then the masthead becomes two rows, then three, and only at
   phone widths does the layout stack. Nothing is ever hidden.
   ========================================================================== */

@media (max-width: 980px) {
    #app {
        grid-template-columns: minmax(0, 1fr);
        /* `auto` for the masthead: it is two bands tall, and the old hard 60px
           cropped it. */
        grid-template-rows: auto auto 1fr;
        height: auto;
        min-height: 100vh;
    }
    /* The masthead keeps `grid-column: 1 / span 2` from the desktop layout, but
       this grid only declares one column — so the span was conjuring an
       implicit second column, `main` was auto-placing into it, and the crew
       panel was left fighting for the remainder. Measured at 860px wide: the
       panel came out 40px across. It has been like that at every stacked width
       since the breakpoint was written. */
    header { grid-column: 1; }
    aside, main { grid-column: 1; }
    body {
        overflow-y: auto;
        overflow-x: hidden;
        height: auto;
    }
    aside {
        border-right: none;
        border-bottom: 1px solid rgba(255, 255, 255, 0.05);
        max-height: 40vh;
    }
    /* The draw column is gone at every width now, so these no longer have a
       two-column layout to undo. The overlay still needs to cover the arena,
       which on a stacked layout means fixing it to the viewport instead of to
       a .play-layout that is now as tall as the whole page. */
    .draw-section {
        position: fixed;
        inset: 0;
    }
    .arena-head {
        flex-wrap: wrap;
        gap: 10px;
    }
    .tide-indicator {
        order: 3;
    }
}

/* Step 2 — measured, not guessed: wordmark + gauges + cluster + tools have a
   floor of ~880px once the clamps above have done their work, so the cluster
   only needs its own row below that. */
@media (max-width: 900px) {
    header {
        grid-template-columns: auto minmax(0, 1fr);
        grid-template-areas:
            "brand  gauges"
            "hud    hud"
            "stats  stats";
    }
    .stats-bar {
        justify-self: stretch;
        justify-content: space-between;
    }
    .stat-cluster { flex: 1 1 auto; }
    .stat-pill { flex: 1 1 0; }
}


/* Short windows.

   Zoom shrinks the viewport in BOTH directions, and a masthead that has grown
   to two rows on a 617px-tall window is eating a fifth of the game. Width
   breakpoints alone cannot see that, so height gets its own pass: the same
   layout, tighter. */
@media (max-height: 760px) {
    :root { --header-min-h: 60px; }
    header { row-gap: 5px; padding-top: 5px; padding-bottom: 5px; }
    .voyage-hud { padding: 3px 10px; gap: 9px; }
    .tide-indicator { padding-top: 3px; padding-bottom: 3px; }
    .stat-pill { padding-top: 3px; padding-bottom: 3px; }
}

@media (max-height: 620px) {
    :root { --header-min-h: 52px; }
    header { row-gap: 3px; }
    /* The sea's mood in words is the first thing that can go: the glyph and the
       fill bar next to it say the same thing without a line of text. */
    .voy-sea { display: none; }
}

@media (max-width: 560px) {
    header {
        /* One column, everything stacked and centred. `flex-direction` used to
           live here and has been dead since the masthead became a grid. */
        grid-template-columns: minmax(0, 1fr);
        grid-template-areas:
            "brand"
            "gauges"
            "hud"
            "stats";
        justify-items: center;
        row-gap: 10px;
        padding: 12px;
    }
    .gauges { flex-wrap: wrap; justify-content: center; }
    #app {
        grid-template-rows: auto auto 1fr;
    }
    .stats-bar {
        flex-wrap: wrap;
        justify-content: center;
        justify-self: center;
    }
    .stat-cluster { flex-wrap: wrap; justify-content: center; }
    #drawStage {
        width: 230px;
        height: 230px;
    }
    .tide-info {
        min-width: 110px;
    }
}

/* ==========================================================================
   Cinematic scene — now a single <canvas>, see src/scene.js
   ========================================================================== */
#oceanScene {
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    display: block;
}

/* ==========================================================================
   Footage backdrop — body-level, shared by the title screen and the deck.
   Two stacked plates crossfade so a tide change dissolves instead of cutting.
   See src/backdrop.js.
   ========================================================================== */
.bg-video {
    position: fixed;
    inset: 0;
    z-index: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    pointer-events: none;
    opacity: 0;
    /* Graded cross-dissolve between clips. Duration mirrors FADE_MS in
       backdrop.js; the ease-in-out keeps the midpoint of the blend soft so the
       switch reads as a gradient wash rather than a linear cut. */
    transition: opacity 1.4s cubic-bezier(0.45, 0, 0.55, 1);
}

.bg-video.is-front { opacity: 1; }

/* Four clips have to read as five tide phases: this carries the colour the
   footage cannot. Sits above the plates, below #app (z-index 2). */
.bg-tint {
    position: fixed;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    opacity: 0;
    /* NO mix-blend-mode here. A full-viewport blend layer sitting on top of a
       playing video forces the compositor to re-rasterise the blend for every
       decoded frame — it was measurably the most expensive single thing on the
       page and the main cause of the stutter players reported in 0.1. A flat
       low-alpha wash carries the same phase colour for free. */
    transition: background 1.4s var(--ease-smooth), opacity 0.9s var(--ease-smooth);
}

body.has-bg-video.bg-in-game .bg-tint { opacity: 0.28; }

/* Frosted glass over a playing video is the same trap: every blurred panel is a
   full-screen readback of live video, every frame, forever. The panels keep
   their translucent backgrounds — they just stop resampling the footage behind
   them. Modals and the locker keep their blur, since those only exist while the
   game is paused behind them. */
body.has-bg-video header,
body.has-bg-video aside,
body.has-bg-video main,
body.has-bg-video .floating-indicator {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

/* The performance preset takes it all the way off, everywhere. */
html[data-perf="save"] header,
html[data-perf="save"] aside,
html[data-perf="save"] main,
html[data-perf="save"] .floating-indicator,
html[data-perf="save"] .modal-overlay,
html[data-perf="save"] .settings-overlay,
html[data-perf="save"] .lk-viewer {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
}
html[data-perf="save"] .bg-tint { opacity: 0.2; }

/* The dimmest step of the text ramp was verified against the flat --bg-void.
   Footage raises the effective background behind every panel, where it
   measured 3.89:1 — under AA. This step is the same hue, lifted until it
   clears 4.5:1 against the brightest frame of all four clips.
   Custom properties resolve from the nearest ancestor, so this would silently
   outrank the html-level high-contrast ramp; the second rule restores it. */
body.has-bg-video { --text-dim: #a09585; }
html[data-contrast="high"] body.has-bg-video { --text-dim: #c6bdb0; }

/* The canvas and the footage are alternatives, never co-tenants — but the
   hiding happens in scene.js, not here: refreshFromSettings() writes
   canvas.style.display inline, and an inline style beats any rule in this
   sheet. A `body.has-bg-video #oceanScene { display: none }` rule here looks
   right and does nothing. */

/* Let the scene show through the app — but always keep panels readable */
#app { background: transparent; }

/* While the title screen is up, the game must not exist on screen. The title
   backdrop is deliberately translucent so the ocean canvas reads through it —
   which also meant the header and sidebar were showing through. Note this is
   keyed off a body class, NOT #app[aria-hidden]: the settings dialog sets
   aria-hidden on #app for inertness, and that must not blank the game. */
body.is-title #app { display: none; }

/* Opacities are set by contrast, not by taste: footage is far brighter than
   the canvas these were tuned against. At the old 0.62, body text over the
   deck lanterns measured 2.76:1 — under AA. */
main {
    background: linear-gradient(180deg, rgba(9, 8, 16, 0.90), rgba(9, 8, 16, 0.86));
    backdrop-filter: blur(10px);
}

aside {
    background: rgba(8, 7, 14, 0.86);
    backdrop-filter: blur(10px);
}

/* ==========================================================================
   Header gauges (tide + tension) & sound toggle
   ========================================================================== */
.gauges {
    display: flex;
    align-items: center;
    gap: 10px;
    /* NOT flex-wrap. Wrapping here is what dropped the mood readout below the
       masthead and over the arena — the header re-flows as whole rows now. */
    flex-wrap: nowrap;
    min-width: 0;
}

.tension-indicator .tide-glyph {
    transition: filter 0.5s ease;
}

.sound-toggle {
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 50%;
    width: 34px;
    height: 34px;
    font-size: 15px;
    cursor: pointer;
    color: var(--text-main);
    transition: transform 0.2s var(--ease-pop), border-color 0.2s ease, box-shadow 0.2s ease;
    display: flex;
    align-items: center;
    justify-content: center;
}

.sound-toggle:hover {
    transform: scale(1.08);
    border-color: var(--frost);
    box-shadow: 0 0 14px rgba(143, 199, 232, 0.35);
}

.sound-toggle.muted {
    opacity: 0.55;
    filter: grayscale(0.6);
}

/* Ease-the-tension action button flavour */
.action-btn.ease-btn {
    color: var(--frost);
    border-color: rgba(143, 199, 232, 0.4);
    background: rgba(143, 199, 232, 0.06);
}
.action-btn.ease-btn:hover:not(:disabled) {
    background: rgba(143, 199, 232, 0.16);
    box-shadow: 0 0 16px rgba(143, 199, 232, 0.35);
}

/* Asking for exclusivity, or taking it back. Sits at the top of the social
   column and has to look like the heaviest thing there. */
.action-btn.pact-btn {
    color: #9fe3c0;
    border-color: rgba(159, 227, 192, 0.45);
    background: rgba(159, 227, 192, 0.07);
}
.action-btn.pact-btn:hover:not(:disabled) {
    background: rgba(159, 227, 192, 0.17);
    box-shadow: 0 0 16px rgba(159, 227, 192, 0.35);
}

@media (max-width: 1100px) {
    .tension-indicator { display: none; }
}

/* ==========================================================================
   A11Y PRIMITIVES
   ========================================================================== */
.visually-hidden {
    position: absolute !important;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.skip-link {
    position: fixed;
    top: 12px;
    left: 12px;
    z-index: 9999;
    padding: 12px 22px;
    border-radius: var(--radius-sm);
    background: var(--ember);
    color: #1a0d06;
    font-family: 'Outfit', sans-serif;
    font-weight: 700;
    font-size: 0.875rem;
    text-decoration: none;
    transform: translateY(-160%);
    transition: transform 0.18s var(--ease-smooth);
}
.skip-link:focus { transform: translateY(0); }

/* A single, unmistakable focus ring everywhere. The old sheet had none at all —
   keyboard users had no idea where they were. */
:focus-visible {
    outline: 3px solid var(--focus-ring);
    outline-offset: 3px;
    border-radius: 4px;
}
:focus:not(:focus-visible) { outline: none; }

/* ==========================================================================
   TITLE SCREEN
   --------------------------------------------------------------------------
   Centred, cinematic composition layered over the live ocean canvas.
   Three things carry it: a moonlight sweep across the wordmark, a rippling
   reflection of that wordmark sitting "on the water", and a menu built from
   typography and light instead of boxes.
   ========================================================================== */
/* The start-of-voyage setup runs WHILE the title screen is still up — see
   Menu.startGame, which returns early and leaves the title in place so the
   choices are made over the water rather than over a blank page. But every
   modal in the game is z-index 1000 and the title is 5000, so setup rendered
   UNDERNEATH it: the panes were visible through the translucent title, and the
   Continue button was unreachable because the title was still eating clicks.
   Only reproducible on a profile that has never run setup (Setup.needed() is
   false ever after), which is exactly the path a first-time player takes. */
#setupModal {
    z-index: 5100;
}

#titleScreen {
    position: fixed;
    inset: 0;
    z-index: 5000;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: clamp(20px, 5vw, 72px);
    /* Single source of truth for the wordmark: the strip of water reserved for
       its reflection is derived from this, so the two can never drift apart.
       Bounded by height as well as width — the composition is vertical, so a
       wide-but-short window must not get a wordmark that eats the menu. */
    --wm-size: clamp(2.5rem, min(8vw, 12vh), 7rem);
    /* Left transparent so the ocean canvas remains the fallback backdrop when
       no video plays. The darkening now lives on .title-scrim, which must sit
       ABOVE the video plate but below the type. */
    background: transparent;
    transition: opacity 0.8s var(--ease-smooth), transform 0.8s var(--ease-smooth);
}

.title-scrim {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    background:
        radial-gradient(ellipse 130% 70% at 50% 118%, rgba(232, 112, 58, 0.13), transparent 62%),
        linear-gradient(180deg,
            rgba(4, 5, 10, 0.90) 0%,
            rgba(4, 5, 10, 0.55) 38%,
            rgba(4, 5, 10, 0.62) 68%,
            rgba(4, 5, 10, 0.92) 100%);
}

/* Deliberately lighter than it needs to be for contrast alone. A scrim heavy
   enough to carry the type on its own crushed the footage to near-black and
   defeated the point of having it; the type gets its own shadow instead. */
#titleScreen.has-video .title-scrim {
    background:
        radial-gradient(ellipse 130% 70% at 50% 118%, rgba(232, 112, 58, 0.14), transparent 62%),
        linear-gradient(180deg,
            rgba(4, 5, 10, 0.82) 0%,
            rgba(4, 5, 10, 0.42) 34%,
            rgba(4, 5, 10, 0.52) 62%,
            rgba(4, 5, 10, 0.89) 100%);
}

/* Local legibility for type sitting on footage. Costs nothing visually on the
   plate and buys far more contrast than darkening the whole frame would. */
#titleScreen.has-video .tm-label,
#titleScreen.has-video .tm-meta,
#titleScreen.has-video .tm-idx,
#titleScreen.has-video .title-footnote,
#titleScreen.has-video .title-byline,
#titleScreen.has-video .title-chapter {
    text-shadow:
        0 1px 2px rgba(0, 0, 0, 0.95),
        0 2px 10px rgba(0, 0, 0, 0.85),
        0 0 28px rgba(0, 0, 0, 0.6);
}

#titleScreen.has-video .title-wordmark {
    filter:
        drop-shadow(0 2px 6px rgba(0, 0, 0, 0.85))
        drop-shadow(0 0 70px rgba(232, 112, 58, 0.30));
}

/* The deck lanterns land directly behind the menu column on the `deck` clip,
   dropping plate contrast under the smallest text to 1.7:1. A wide, heavily
   feathered pool of shade fixes that spot without dimming the lanterns at the
   frame edges, which are the best thing in the shot. */
#titleScreen.has-video .title-menu { position: relative; }

#titleScreen.has-video .title-menu::before {
    content: "";
    position: absolute;
    inset: -44px -150px;
    z-index: -1;
    pointer-events: none;
    background: radial-gradient(ellipse at center,
        rgba(4, 5, 10, 0.84) 0%,
        rgba(4, 5, 10, 0.70) 55%,
        rgba(4, 5, 10, 0.44) 75%,
        rgba(4, 5, 10, 0.16) 90%,
        transparent 100%);
}

#titleScreen.is-leaving {
    opacity: 0;
    transform: scale(1.05);
    pointer-events: none;
}

/* Soft moon high on the right — the light source everything else answers to. */
.title-moon {
    position: absolute;
    z-index: 2;
    top: -14vh;
    right: -6vw;
    width: min(58vw, 620px);
    aspect-ratio: 1;
    pointer-events: none;
    background: radial-gradient(circle at 50% 50%,
        rgba(214, 232, 248, 0.15) 0%,
        rgba(179, 205, 232, 0.07) 34%,
        transparent 68%);
    animation: moonDrift 24s ease-in-out infinite;
}

.title-vignette {
    position: absolute;
    inset: 0;
    z-index: 3;
    pointer-events: none;
    box-shadow: inset 0 0 clamp(90px, 18vw, 260px) rgba(0, 0, 0, 0.85);
}

.title-inner {
    position: relative;
    z-index: 4;
    width: min(660px, 100%);
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
}

/* --- Emblem: small caps flanked by hairlines --- */
/* --- Wordmark + its reflection --- */
.title-mark {
    position: relative;
    /* Reserves the strip of water the reflection falls onto. The mask below
       fades the reflection out by ~42% of its height, so half the wordmark
       size always clears it. */
    margin-bottom: calc(var(--wm-size) * 0.5);
    animation: wordmarkIn 1.4s var(--ease-smooth) 0.22s both;
}

.title-wordmark,
.title-reflection {
    font-family: 'Cinzel', serif;
    font-weight: 800;
    font-size: var(--wm-size);
    line-height: 0.94;
    letter-spacing: 0.015em;
    white-space: nowrap;
}

.title-wordmark {
    /* Cold moonlight, a hot ember band, cold again — the band travels. */
    background: linear-gradient(100deg,
        #7c8ea6 0%,
        #c9dcec 20%,
        #fff6e8 32%,
        #ffd9a3 40%,
        #e8a05a 48%,
        #c9dcec 68%,
        #7c8ea6 100%);
    background-size: 280% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
    filter: drop-shadow(0 0 70px rgba(232, 112, 58, 0.30));
    animation: moonSweep 12s ease-in-out infinite;
}

.title-reflection {
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    /* Mirror in place: origin must be the centre, or the flip lifts the
       reflection up over the wordmark instead of laying it below. */
    transform: scaleY(-1);
    transform-origin: 50% 50%;
    color: rgba(178, 205, 232, 0.26);
    filter: blur(2.5px);
    pointer-events: none;
    /* Mask lives in un-flipped local space: the local BOTTOM ends up adjacent
       to the wordmark once scaleY(-1) is applied, so that is the opaque end.
       It dies out by ~45% so the reflection never reaches the divider. */
    -webkit-mask-image: linear-gradient(to bottom, transparent 58%, rgba(0, 0, 0, 0.30) 78%, rgba(0, 0, 0, 0.82) 100%);
    mask-image: linear-gradient(to bottom, transparent 58%, rgba(0, 0, 0, 0.30) 78%, rgba(0, 0, 0, 0.82) 100%);
    animation: waterWobble 7s ease-in-out infinite;
}

/* --- Divider: hairline · diamond · hairline --- */
/* --- Menu: typography and light, no cards --- */
.title-menu {
    width: min(480px, 100%);
    /* Last holder of the gap under the wordmark, after the tagline and the
       divider that used to sit here both came out. */
    margin-top: clamp(24px, 4.5vh, 40px);
    display: flex;
    flex-direction: column;
    gap: 2px;
    animation: titleIn 1s var(--ease-smooth) 0.75s both;
}

.title-menu-btn {
    position: relative;
    display: grid;
    grid-template-columns: 30px 1fr 22px;
    align-items: center;
    gap: 14px;
    padding: 12px 18px;
    border: 0;
    border-radius: 10px;
    background: transparent;
    color: var(--text-muted);
    text-align: left;
    font: inherit;
    cursor: pointer;
    isolation: isolate;
    transition: color 0.25s var(--ease-smooth), transform 0.25s var(--ease-smooth);
}

/* Light bleeding in from the left edge on hover/focus. */
.title-menu-btn::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    background: linear-gradient(90deg,
        rgba(232, 112, 58, 0.20) 0%,
        rgba(156, 43, 78, 0.10) 38%,
        transparent 82%);
    opacity: 0;
    transition: opacity 0.25s var(--ease-smooth);
}

/* The ember bar that grows along the left edge. */
.title-menu-btn::after {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    width: 2px;
    height: 62%;
    border-radius: 2px;
    background: linear-gradient(180deg, var(--amber), var(--ember), var(--wine));
    box-shadow: 0 0 12px rgba(232, 112, 58, 0.8);
    transform: translateY(-50%) scaleY(0);
    transition: transform 0.28s var(--ease-pop);
}

.title-menu-btn:hover:not(:disabled),
.title-menu-btn:focus-visible {
    color: var(--text-main);
    transform: translateX(5px);
}
.title-menu-btn:hover:not(:disabled)::before,
.title-menu-btn:focus-visible::before { opacity: 1; }
.title-menu-btn:hover:not(:disabled)::after,
.title-menu-btn:focus-visible::after { transform: translateY(-50%) scaleY(1); }

.title-menu-btn.is-primary { color: var(--text-main); }
.title-menu-btn.is-primary::before { opacity: 0.55; }
.title-menu-btn.is-primary::after { transform: translateY(-50%) scaleY(1); }

.title-menu-btn:disabled {
    opacity: 0.34;
    cursor: not-allowed;
}

/* --- Outbound links: web and Patreon ---
   The main menu is deliberately card-less typography. These two go the other
   way — bordered pills, an icon each — precisely so they never read as a fourth
   and fifth way to start playing. */
.title-links {
    width: min(480px, 100%);
    margin-top: clamp(18px, 3vh, 28px);
    padding-top: clamp(14px, 2.4vh, 20px);
    border-top: 1px solid var(--border-soft);
    animation: titleIn 1s var(--ease-smooth) 0.85s both;
}

.title-byline {
    margin-bottom: 10px;
    padding-left: 2px;
    font-size: 0.6875rem;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--text-dim);
}
.title-byline strong {
    font-weight: 600;
    color: var(--gold);
    letter-spacing: 0.18em;
}

.title-links-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
}

.title-link {
    position: relative;
    display: grid;
    grid-template-columns: 26px 1fr 14px;
    align-items: center;
    gap: 10px;
    padding: 11px 13px;
    border: 1px solid var(--border-soft);
    border-radius: 12px;
    background: rgba(255, 255, 255, 0.03);
    color: var(--text-muted);
    text-decoration: none;
    overflow: hidden;
    transition: border-color 0.25s var(--ease-smooth),
                background 0.25s var(--ease-smooth),
                color 0.25s var(--ease-smooth),
                transform 0.25s var(--ease-pop);
}

.title-link:hover,
.title-link:focus-visible {
    transform: translateY(-2px);
    border-color: rgba(230, 192, 101, 0.45);
    background: rgba(255, 255, 255, 0.06);
    color: var(--text-main);
}

/* Patreon is the ask, so it carries the warm fill and the only glow. */
.title-link.is-support {
    border-color: rgba(232, 112, 58, 0.38);
    background: linear-gradient(120deg,
        rgba(232, 112, 58, 0.16),
        rgba(156, 43, 78, 0.10));
    color: var(--text-main);
}
.title-link.is-support:hover,
.title-link.is-support:focus-visible {
    border-color: rgba(232, 112, 58, 0.75);
    background: linear-gradient(120deg,
        rgba(232, 112, 58, 0.28),
        rgba(156, 43, 78, 0.18));
    box-shadow: 0 6px 22px rgba(232, 112, 58, 0.22);
}

.tl-icon {
    width: 26px;
    height: 26px;
    padding: 4px;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.05);
    fill: none;
    stroke: var(--gold);
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
    opacity: 0.85;
    transition: opacity 0.25s var(--ease-smooth);
}
.title-link:hover .tl-icon,
.title-link:focus-visible .tl-icon { opacity: 1; }

/* The heart reads as a shape, not an outline. */
.title-link.is-support .tl-icon {
    background: rgba(232, 112, 58, 0.14);
    fill: var(--ember);
    stroke: none;
}

.tl-text {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}

.tl-label {
    font-family: 'Outfit', sans-serif;
    font-weight: 600;
    font-size: 0.8125rem;
    letter-spacing: 0.01em;
    color: inherit;
    /* Long enough to wrap on a narrow rail; a domain should never do that. */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.tl-meta {
    font-size: 0.6875rem;
    color: var(--text-dim);
}

.tl-arrow {
    font-size: 0.75rem;
    color: var(--gold);
    opacity: 0.55;
    transition: opacity 0.25s var(--ease-smooth), transform 0.25s var(--ease-pop);
}
.title-link:hover .tl-arrow,
.title-link:focus-visible .tl-arrow {
    opacity: 1;
    transform: translate(2px, -2px);
}

.tm-idx {
    font-family: 'Cinzel', serif;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    color: var(--gold);
    opacity: 0.65;
    text-align: center;
    transition: opacity 0.25s var(--ease-smooth);
}
.title-menu-btn:hover:not(:disabled) .tm-idx,
.title-menu-btn:focus-visible .tm-idx,
.title-menu-btn.is-primary .tm-idx { opacity: 1; }

.tm-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.tm-label {
    font-family: 'Outfit', sans-serif;
    font-weight: 600;
    font-size: 0.9375rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
}

.tm-meta {
    font-size: 0.8125rem;
    letter-spacing: 0.01em;
    /* --text-dim measured 2.87:1 over the lit deck footage — under AA. This
       ramp step clears it with room to spare on all four clips. */
    color: var(--text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
}

.tm-arrow {
    font-size: 1rem;
    color: var(--ember);
    opacity: 0;
    transform: translateX(-6px);
    transition: opacity 0.25s var(--ease-smooth), transform 0.25s var(--ease-pop);
}
.title-menu-btn:hover:not(:disabled) .tm-arrow,
.title-menu-btn:focus-visible .tm-arrow {
    opacity: 1;
    transform: translateX(0);
}

/* --- Footer rail --- */
.title-foot {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 4;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 18px clamp(20px, 5vw, 46px);
    border-top: 1px solid rgba(255, 255, 255, 0.05);
    animation: titleIn 1s var(--ease-smooth) 0.95s both;
}

.title-footnote,
.title-chapter {
    font-size: 0.75rem;
    color: var(--text-dim);
}

.title-chapter {
    font-family: 'Cinzel', serif;
    letter-spacing: 0.24em;
    text-transform: uppercase;
}

.title-footnote kbd {
    display: inline-block;
    padding: 2px 7px;
    border: 1px solid var(--border-soft);
    border-bottom-width: 2px;
    border-radius: 4px;
    background: rgba(255, 255, 255, 0.05);
    font-family: inherit;
    font-size: 0.6875rem;
    color: var(--text-muted);
}

@media (max-width: 620px) {
    .title-chapter { display: none; }
    .title-menu-btn { grid-template-columns: 24px 1fr 0; gap: 10px; }
    .tm-arrow { display: none; }
    /* Side by side, the domain would ellipsis away to nothing. Stack instead,
       and claw back the height the second row costs so the block keeps clear
       of the footer rail. */
    .title-links-row { grid-template-columns: 1fr; gap: 8px; }
    .title-links { margin-top: 14px; padding-top: 12px; }
}

/* Short viewports: the composition is vertical, so height is the binding
   constraint long before width is. Tighten rhythm rather than let the menu
   run under the footer rail. */
@media (max-height: 780px) {
    #titleScreen { padding-block: 32px; }
    .title-menu { margin-top: 22px; }
    .title-menu-btn { padding: 9px 16px; }
    .tm-meta { font-size: 0.75rem; }
    .title-foot { padding-block: 12px; }
    .title-link { padding: 9px 12px; }
}

@media (max-height: 660px) {
    #titleScreen { padding-block: 24px; }
    .title-menu { margin-top: 18px; }
    .title-menu-btn { padding: 7px 16px; }
    .tm-label { font-size: 0.875rem; }
    .tm-meta { font-size: 0.72rem; }
    /* Height is the binding constraint here: the pills keep their labels but
       drop the second line rather than push the menu under the footer rail. */
    .title-links { margin-top: 14px; padding-top: 12px; }
    .title-byline { margin-bottom: 7px; }
    .title-link { padding: 8px 12px; }
    .tl-meta { display: none; }
}

@keyframes titleIn {
    from { opacity: 0; transform: translateY(16px); }
    to   { opacity: 1; transform: none; }
}

@keyframes wordmarkIn {
    from { opacity: 0; transform: translateY(26px) scale(0.96); filter: blur(14px); }
    to   { opacity: 1; transform: none; filter: blur(0); }
}

@keyframes moonSweep {
    0%, 100% { background-position: 118% 50%; }
    50%      { background-position: -18% 50%; }
}

@keyframes waterWobble {
    0%, 100% { transform: scaleY(-1) skewX(0deg)     translateY(0); }
    35%      { transform: scaleY(-1) skewX(0.9deg)   translateY(1.5px); }
    70%      { transform: scaleY(-1) skewX(-0.75deg) translateY(-1px); }
}

@keyframes moonDrift {
    0%, 100% { transform: translate3d(0, 0, 0)      scale(1); }
    50%      { transform: translate3d(-2%, 3%, 0)   scale(1.06); }
}

/* ==========================================================================
   SETTINGS DIALOG
   ========================================================================== */
.settings-overlay {
    position: fixed;
    inset: 0;
    z-index: 6000;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: rgba(3, 4, 8, 0.78);
    backdrop-filter: blur(8px);
}
.settings-overlay.is-open { display: flex; }

.settings-card {
    width: min(720px, 100%);
    max-height: min(85vh, 860px);
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-lg);
    background: #0d0f18;
    box-shadow: 0 30px 90px rgba(0, 0, 0, 0.7);
    animation: settingsIn 0.32s var(--ease-smooth);
}

@keyframes settingsIn {
    from { opacity: 0; transform: translateY(20px) scale(0.985); }
    to   { opacity: 1; transform: none; }
}

.settings-head {
    position: relative;
    padding: 26px 28px 20px;
    border-bottom: 1px solid var(--border-soft);
}

.settings-head h2 {
    font-family: 'Outfit', sans-serif;
    font-size: 1.375rem;
    font-weight: 700;
    color: var(--text-main);
}

.settings-sub {
    margin-top: 4px;
    font-size: 0.875rem;
    color: var(--text-dim);
}

.settings-close {
    position: absolute;
    top: 20px;
    right: 20px;
    width: 38px;
    height: 38px;
    border: 1px solid var(--border-soft);
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.04);
    color: var(--text-muted);
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
    transition: background 0.2s, color 0.2s;
}
.settings-close:hover { background: rgba(255, 255, 255, 0.1); color: var(--text-main); }

.settings-body {
    overflow-y: auto;
    padding: 8px 28px 20px;
}

.settings-group { padding: 20px 0; border-bottom: 1px solid rgba(255, 255, 255, 0.05); }
.settings-group:last-child { border-bottom: 0; }

.settings-group h3 {
    font-family: 'Outfit', sans-serif;
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--amber-soft);
}

.settings-group-hint {
    margin-top: 5px;
    font-size: 0.8125rem;
    color: var(--text-dim);
}

.settings-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 24px;
    padding: 15px 0;
}

.settings-label { flex: 1; min-width: 0; }

.settings-label-text {
    display: block;
    font-size: 0.9375rem;
    color: var(--text-main);
}

.settings-help {
    display: block;
    margin-top: 3px;
    font-size: 0.8125rem;
    color: var(--text-dim);
    max-width: 46ch;
}

/* --- Switch --- */
.settings-switch {
    flex-shrink: 0;
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
}

.switch-track {
    display: block;
    width: 50px;
    height: 28px;
    padding: 3px;
    border: 1px solid var(--border-soft);
    border-radius: 100px;
    background: rgba(255, 255, 255, 0.06);
    transition: background 0.2s var(--ease-smooth), border-color 0.2s;
}

.switch-thumb {
    display: block;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--text-dim);
    transition: transform 0.22s var(--ease-pop), background 0.22s;
}

.settings-switch.is-on .switch-track {
    background: var(--ember);
    border-color: var(--ember);
}
.settings-switch.is-on .switch-thumb {
    transform: translateX(22px);
    background: #fff;
}

/* --- Segmented --- */
.settings-segmented {
    flex-shrink: 0;
    display: flex;
    padding: 3px;
    border: 1px solid var(--border-soft);
    border-radius: 100px;
    background: rgba(255, 255, 255, 0.04);
}

.seg-btn {
    padding: 7px 15px;
    border: 0;
    border-radius: 100px;
    background: none;
    color: var(--text-muted);
    font: inherit;
    font-size: 0.8125rem;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.2s, color 0.2s;
}
.seg-btn:hover { color: var(--text-main); }
.seg-btn.is-on {
    background: var(--ember);
    color: #1a0d06;
    font-weight: 600;
}

/* --- Slider --- */
.settings-slider {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 14px;
}

.settings-slider input[type="range"] {
    width: 160px;
    accent-color: var(--ember);
    cursor: pointer;
}

.slider-value {
    min-width: 4ch;
    font-family: 'Outfit', sans-serif;
    font-size: 0.875rem;
    font-variant-numeric: tabular-nums;
    color: var(--text-main);
}

.settings-foot {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    padding: 18px 28px;
    border-top: 1px solid var(--border-soft);
}

.settings-btn {
    padding: 11px 26px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border-soft);
    font: inherit;
    font-family: 'Outfit', sans-serif;
    font-weight: 600;
    font-size: 0.875rem;
    cursor: pointer;
    transition: background 0.2s, border-color 0.2s;
}
.settings-btn.ghost { background: none; color: var(--text-muted); }
.settings-btn.ghost:hover { background: rgba(255, 255, 255, 0.06); color: var(--text-main); }
.settings-btn.primary { background: var(--ember); border-color: var(--ember); color: #1a0d06; }
.settings-btn.primary:hover { background: var(--ember-soft); }

@media (max-width: 600px) {
    .settings-row { flex-direction: column; align-items: stretch; gap: 12px; }
    .settings-slider input[type="range"] { width: 100%; }
}

/* ==========================================================================
   ACCESSIBILITY PREFERENCE LAYER
   Driven by data-* attributes that settings.js writes onto <html>.
   ========================================================================== */

/* --- Motion ------------------------------------------------------------- */
html[data-motion="reduced"] *,
html[data-motion="reduced"] *::before,
html[data-motion="reduced"] *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 120ms !important;
}

html[data-motion="none"] *,
html[data-motion="none"] *::before,
html[data-motion="none"] *::after {
    animation: none !important;
    transition: none !important;
    scroll-behavior: auto !important;
}

/* Effects that exist purely to move: strip them rather than speed them up. */
html[data-motion="reduced"] .title-menu-btn:hover,
html[data-motion="none"] .title-menu-btn:hover,
html[data-motion="reduced"] .title-link:hover,
html[data-motion="none"] .title-link:hover { transform: none; }

/* Without the sweep the wordmark would freeze on the cold tail of its gradient.
   Park the warm band across the middle instead, so it still reads as lit. */
html[data-motion="reduced"] .title-wordmark,
html[data-motion="none"] .title-wordmark { background-position: 50% 50%; }

html[data-motion="none"] .title-moon { display: none; }

html[data-shake="off"] #app,
html[data-shake="off"] .modal-card { animation-name: none !important; }

html[data-flashes="off"] #flashOverlay,
html[data-flashes="off"] #confettiLayer { display: none !important; }

/* --- Contrast ----------------------------------------------------------- */
html[data-contrast="high"] {
    --text-main: #ffffff;
    --text-muted: #e4ddd2;
    --text-dim: #c6bdb0;
    --border-soft: rgba(255, 255, 255, 0.35);
    --card-bg: rgba(8, 8, 12, 0.96);
    --card-bg-solid: #06070c;
    --focus-ring: #ffd28a;
}

/* Translucency is the enemy of contrast — make every surface opaque. */
html[data-contrast="high"] main,
html[data-contrast="high"] aside,
html[data-contrast="high"] .settings-card,
html[data-contrast="high"] .modal-card {
    background: #06070c !important;
    backdrop-filter: none !important;
}

html[data-contrast="high"] #oceanScene { opacity: 0.32; }

html[data-contrast="high"] .nav-btn,
html[data-contrast="high"] .action-btn,
html[data-contrast="high"] .tab-btn,
html[data-contrast="high"] .title-menu-btn,
html[data-contrast="high"] .title-link,
html[data-contrast="high"] .seg-btn {
    border: 1px solid rgba(255, 255, 255, 0.45);
}

html[data-contrast="high"] :focus-visible { outline-width: 4px; }

/* --- Typography --------------------------------------------------------- */
/* The evidence for dyslexia support is strongest for generous letter, word and
   line spacing; the typeface itself matters less. We do both. */
html[data-dyslexia="on"] body,
html[data-dyslexia="on"] button,
html[data-dyslexia="on"] input,
html[data-dyslexia="on"] select {
    font-family: "OpenDyslexic", "Atkinson Hyperlegible", Verdana, Tahoma, sans-serif !important;
    letter-spacing: 0.045em;
    word-spacing: 0.14em;
}

html[data-dyslexia="on"] .title-wordmark,
html[data-dyslexia="on"] h1,
html[data-dyslexia="on"] h2 {
    font-family: Verdana, Tahoma, sans-serif !important;
}

/* Long-form text should never carry display letter-spacing. */
html[data-dyslexia="on"] .title-byline,
html[data-dyslexia="on"] .aside-title { letter-spacing: 0.08em; }

html[data-spacing="tight"]  { --line-base: 1.4; }
html[data-spacing="normal"] { --line-base: 1.6; }
html[data-spacing="loose"]  { --line-base: 1.95; }

body,
.bubble-text,
.narrative-line,
.modal-desc,
.epilogue-text-box,
.settings-help,
.intro-banner p {
    line-height: var(--line-base);
}

html[data-spacing="loose"] .bubble-text,
html[data-spacing="loose"] .narrative-line,
html[data-spacing="loose"] .epilogue-text-box p { margin-bottom: 0.85em; }

html[data-underline="on"] a { text-decoration: underline !important; }
/* The external title entries are anchors dressed as menu rows: underlining the
   whole grid draws a rule across index, label and arrow alike. Mark the label. */
html[data-underline="on"] .title-link { text-decoration: none !important; }
html[data-underline="on"] .title-link .tl-label { text-decoration: underline; }

/* --- Density ------------------------------------------------------------ */
html[data-density="compact"] .settings-row { padding: 9px 0; }
html[data-density="compact"] .settings-group { padding: 13px 0; }
html[data-density="compact"] .sidebar-player-card { padding: 8px 10px; }
html[data-density="compact"] .panel-content { padding: 14px; }
html[data-density="compact"] .settings-help { display: none; }

/* --- Content ------------------------------------------------------------ */
html[data-blurimages="on"] .scene-visual-img,
html[data-blurimages="on"] .modal-img {
    filter: blur(22px);
    transition: filter 0.25s var(--ease-smooth);
}
html[data-blurimages="on"] .scene-visual-img:hover,
html[data-blurimages="on"] .modal-img:hover,
html[data-blurimages="on"] .visual-image-wrapper:focus-within .scene-visual-img {
    filter: none;
}

/* --- Type scale --------------------------------------------------------- */
/* Expressed in rem so the text-size setting genuinely moves them. */
body { font-size: 1rem; }
.bubble-text     { font-size: 0.9375rem; }
.narrative-line  { font-size: 0.9375rem; }
.modal-desc      { font-size: 0.9375rem; }
.nav-btn,
.action-btn,
.gallery-btn     { font-size: 0.875rem; }
.tab-btn         { font-size: 0.9375rem; }
.stat-pill       { font-size: 0.8125rem; }
.player-name-lbl { font-size: 0.9375rem; }
.epilogue-text-box { font-size: 0.9375rem; }
.persistent-log-entry { font-size: 0.875rem; }

/* Extreme tracking is fine on a 4-word label, painful on a sentence. */
.intro-banner p,
.modal-desc,
.bubble-text { letter-spacing: normal; }

/* ==========================================================================
   PHASE 2 — MECHANICS UI
   ========================================================================== */

/* --- Header pills -------------------------------------------------------- */
/* The two cells that drive decisions get colour; the tallies stay neutral so
   they do not compete with them. */
.stat-pill.stat-sway { background: rgba(230, 192, 101, 0.09); }
.stat-pill.stat-sway .stat-val { color: var(--gold); }
.stat-pill.stat-sway .stat-key { color: rgba(230, 192, 101, 0.72); }

.stat-pill.stat-clock .stat-val { color: var(--frost); }

.stat-pill.stat-clock.is-urgent {
    background: rgba(255, 107, 96, 0.14);
}
.stat-pill.stat-clock.is-urgent .stat-val {
    color: var(--danger);
    animation: urgentPulse 1.6s ease-in-out infinite;
}
.stat-pill.stat-clock.is-urgent .stat-key { color: rgba(255, 107, 96, 0.8); }

@keyframes urgentPulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.55; }
}

/* --- Sway panel ---------------------------------------------------------- */
.sway-panel {
    width: 100%;
    margin-top: 14px;
    padding: 14px 16px;
    border: 1px solid rgba(230, 192, 101, 0.22);
    border-radius: var(--radius-md);
    background: rgba(230, 192, 101, 0.05);
}

.sway-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 11px;
}

.sway-title {
    font-family: 'Outfit', sans-serif;
    font-weight: 600;
    font-size: 0.8125rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--gold);
}

.sway-count {
    font-size: 0.8125rem;
    color: var(--text-dim);
}

.sway-row {
    display: flex;
    flex-wrap: wrap;
    gap: 9px;
}

.sway-btn {
    display: flex;
    flex-direction: column;
    gap: 2px;
    align-items: flex-start;
    padding: 10px 16px;
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-sm);
    background: rgba(255, 255, 255, 0.04);
    color: var(--text-main);
    font: inherit;
    cursor: pointer;
    transition: background 0.18s var(--ease-smooth),
                border-color 0.18s var(--ease-smooth),
                transform 0.18s var(--ease-smooth);
}

.sway-btn:hover:not(:disabled) {
    background: rgba(230, 192, 101, 0.12);
    border-color: rgba(230, 192, 101, 0.45);
    transform: translateY(-2px);
}

.sway-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.sway-btn-label {
    font-family: 'Outfit', sans-serif;
    font-weight: 600;
    font-size: 0.9375rem;
}

.sway-btn-cost {
    font-size: 0.75rem;
    color: var(--text-dim);
}

.sway-warn {
    color: var(--danger);
    font-weight: 600;
}

.sway-cancel { border-style: dashed; }

/* --- Action requirement badges ------------------------------------------ */
.req-badge.req-ok {
    color: var(--gold);
    border-color: rgba(230, 192, 101, 0.4);
}

.action-btn.is-locked { opacity: 0.62; }

/* --- Sidebar: traits, neglect, departure -------------------------------- */
.player-trait-note {
    margin-top: 6px;
    font-size: 0.75rem;
    line-height: 1.4;
    color: var(--text-dim);
    font-style: italic;
}

/* Only rendered once somebody is no longer with who they boarded with, so it
   should read as a status change rather than as more flavour text. */
.player-bond-note {
    margin-top: 6px;
    padding: 4px 8px;
    border-radius: 4px;
    background: rgba(255, 217, 122, 0.10);
    border-left: 2px solid #ffd97a;
    font-size: 0.75rem;
    line-height: 1.4;
    color: #ffe3a6;
}

.player-neglect-note {
    margin-top: 6px;
    padding: 4px 8px;
    border-radius: 4px;
    background: rgba(255, 107, 96, 0.12);
    border-left: 2px solid var(--danger);
    font-size: 0.75rem;
    color: #ffb0a8;
}

.player-departed-note {
    margin-top: 6px;
    padding: 4px 8px;
    border-radius: 4px;
    background: rgba(255, 255, 255, 0.05);
    font-size: 0.75rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.sidebar-player-card.is-departed {
    opacity: 0.45;
    filter: grayscale(0.7);
}

/* --- Ending confirmation ------------------------------------------------ */
.confirm-warning {
    margin-top: 10px;
    padding: 12px 14px;
    border-radius: var(--radius-sm);
    border-left: 3px solid var(--amber);
    background: rgba(255, 171, 61, 0.1);
    color: var(--amber-soft);
    text-align: left;
}

.third-relation {
    color: var(--text-dim);
    font-size: 0.8125rem;
}

/* ==========================================================================
   THE VOYAGE — survival HUD, sea readout
   ========================================================================== */
.sea-readout .sea-label {
    font-size: 9px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--text-muted);
    opacity: 0.75;
}
.sea-readout .tide-phase-name { font-style: italic; }

.voyage-hud {
    display: flex;
    align-items: center;
    gap: clamp(8px, 1vw, 14px);
    /* One line, always. It has its own band now, and letting it wrap in place
       was what made the masthead's height a function of the zoom level. When
       the room runs out the strip scrolls sideways instead of growing. */
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    min-width: 0;
    padding: 6px 12px;
    border-radius: 12px;
    background: linear-gradient(180deg, rgba(10, 16, 26, 0.7), rgba(8, 10, 18, 0.5));
    border: 1px solid rgba(143, 199, 232, 0.16);
    box-shadow: inset 0 0 18px rgba(0, 0, 0, 0.4);
}
.voyage-hud::-webkit-scrollbar { display: none; }
.voyage-hud > * { flex: 0 0 auto; }
.voy-day { font-family: 'Outfit', sans-serif; font-size: 12px; color: var(--text-muted); letter-spacing: 0.04em; white-space: nowrap; }
.voy-day b { color: var(--frost); font-size: 15px; }
/* Flavour text, and the first thing that should give ground: it truncates so
   the numbers beside it never have to. */
.voy-sea {
    font-size: 11px; font-style: italic; color: var(--text-muted);
    max-width: clamp(70px, 9vw, 160px);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    flex: 0 1 auto;
}
.voy-res { display: flex; gap: 8px; }
.vres {
    display: flex; align-items: center; gap: 3px;
    padding: 3px 8px; border-radius: 999px;
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.08);
    font-size: 12px;
}
.vres-n { font-weight: 700; color: var(--text-main); }
.vres.is-low { border-color: rgba(255, 90, 90, 0.5); background: rgba(255, 60, 60, 0.12); animation: lowPulse 1.6s ease-in-out infinite; }
@keyframes lowPulse { 0%,100% { opacity: 0.6; } 50% { opacity: 1; } }
/* Deck works: one glyph per rig, dimmed when unbuilt, alarmed when broken, plus
   the button that opens the build/repair panel. */
.voy-rigs { display: flex; align-items: center; gap: 5px; }
.vrig {
    font-size: 13px;
    line-height: 1;
    padding: 3px 5px;
    border-radius: 8px;
    border: 1px solid transparent;
    cursor: default;
}
.vrig.is-none { opacity: 0.28; filter: grayscale(1); }
.vrig.is-ok { background: rgba(60, 210, 130, 0.12); border-color: rgba(60, 210, 130, 0.32); }
.vrig.is-broken {
    background: rgba(255, 90, 90, 0.14);
    border-color: rgba(255, 90, 90, 0.45);
    animation: lowPulse 1.6s ease-in-out infinite;
}
.voy-works-btn {
    margin-left: 2px;
    padding: 3px 10px;
    border-radius: 999px;
    font-family: 'Outfit', sans-serif;
    font-size: 11px;
    letter-spacing: 0.04em;
    color: var(--text-main);
    background: rgba(143, 199, 232, 0.1);
    border: 1px solid rgba(143, 199, 232, 0.3);
    cursor: pointer;
    transition: background 0.2s ease, border-color 0.2s ease;
}
.voy-works-btn:hover { background: rgba(143, 199, 232, 0.22); border-color: rgba(143, 199, 232, 0.55); }
.voy-works-btn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

.voy-strain { display: flex; gap: 8px; }
.vstrain { display: flex; align-items: center; gap: 4px; }
.vstrain-t { width: 54px; height: 6px; border-radius: 3px; background: rgba(255,255,255,0.1); overflow: hidden; }
.vstrain-t i { display: block; height: 100%; border-radius: 3px; transition: width 0.4s ease; }

@media (max-width: 900px) {
    .voyage-hud { width: 100%; justify-content: space-between; }
    .voy-sea { display: none; }
}

/* Landing-party decision box (reuses .finish-choice-box) */
.landfall-box .lf-intro { color: var(--text-muted); font-size: 13px; margin: 4px 0 12px; }
.landfall-opt {
    display: flex; flex-direction: column; align-items: flex-start;
    text-align: left; gap: 2px; width: 100%; margin-bottom: 8px;
    padding: 11px 16px;
}
.landfall-opt .lf-lbl { font-weight: 700; font-size: 14px; }
.landfall-opt .lf-sub { font-size: 11.5px; color: var(--text-muted); font-weight: 400; }
.landfall-opt:disabled { opacity: 0.4; cursor: not-allowed; }

/* Sidebar: dead crew + mood line */
.sidebar-player-card.is-dead { filter: grayscale(1) brightness(0.6); opacity: 0.7; }
.sidebar-player-card.is-dead .player-avatar { box-shadow: none; }
.player-mood-note {
    font-size: 11px; font-style: italic; color: var(--text-muted);
    margin: 2px 0 6px; opacity: 0.85;
}

/* ==========================================================================
   RELATIONSHIP NETWORK
   ========================================================================== */
.relnet-wrap { display: flex; flex-direction: column; align-items: center; gap: 16px; padding: 8px 0 24px; }
.relnet-hint {
    max-width: 460px; margin: 0; text-align: center;
    font-size: 12px; line-height: 1.55; color: var(--text-muted);
}
.relnet-web {
    /* max-width must be 100%, not a vw value: the panel this lives in has its
       own padding, so 92vw overflowed it on narrow screens. */
    position: relative; width: 460px; max-width: 100%; aspect-ratio: 1 / 1;
    margin: 8px auto 26px;
}
.relnet-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.relnet-node {
    position: absolute; transform: translate(-50%, -50%);
    width: min(84px, 18vw); height: min(84px, 18vw); border-radius: 50%;
    padding: 0; cursor: pointer; background: #10121c;
    border: 3px solid var(--nc, #fff);
    box-shadow: 0 0 0 4px rgba(0,0,0,0.4), 0 6px 20px rgba(0,0,0,0.5);
    transition: transform 0.2s var(--ease-pop), box-shadow 0.2s ease;
    overflow: visible;
}
.relnet-node:hover { transform: translate(-50%, -50%) scale(1.08); }
.relnet-node.is-sel { box-shadow: 0 0 0 4px var(--nc, #fff), 0 0 26px var(--nc, #fff); }
.relnet-node.is-dead { filter: grayscale(1) brightness(0.5); }
.relnet-node.is-gone { opacity: 0.45; filter: grayscale(0.7); }
.relnet-face { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; object-position: center top; display: block; }
/* The spite marker: this person has stopped grieving and started wanting. */
.relnet-flag {
    position: absolute; top: -4px; right: -6px; font-size: 15px;
    filter: drop-shadow(0 1px 3px #000);
}
.relnet-name {
    position: absolute; top: calc(100% + 6px); left: 50%; transform: translateX(-50%);
    display: flex; flex-direction: column; align-items: center; gap: 1px;
    font-size: 12px; font-weight: 700; white-space: nowrap; color: var(--text-main);
    text-shadow: 0 1px 4px #000;
}
.relnet-status {
    font-size: 10px; font-weight: 500; font-style: normal;
    color: var(--text-muted); opacity: 0.9;
}

.relnet-legend {
    display: flex; gap: 8px 16px; flex-wrap: wrap; justify-content: center;
    font-size: 11.5px; color: var(--text-muted); max-width: 520px;
}
.relnet-legend .lg-grp { display: inline-flex; align-items: center; gap: 5px; }
/* An arrow swatch, so the legend says "directed" without needing a caption. */
.relnet-legend i.arrow {
    width: 20px; height: 3px; border-radius: 2px; display: inline-block; position: relative;
}
.relnet-legend i.arrow::after {
    content: ""; position: absolute; right: -1px; top: 50%; transform: translateY(-50%);
    border-left: 6px solid currentColor; border-top: 4px solid transparent;
    border-bottom: 4px solid transparent; color: inherit;
}
.relnet-legend i.arrow { color: inherit; }
.relnet-legend i.ring {
    width: 12px; height: 12px; border-radius: 50%; display: inline-block;
    border: 2.5px solid; background: none;
}
.relnet-legend i.band { width: 18px; height: 6px; border-radius: 3px; display: inline-block; opacity: 0.5; }

.relnet-thoughts {
    width: 100%; max-width: 560px;
    background: var(--card-bg-solid);
    border: 1px solid rgba(255,255,255,0.08);
    border-radius: 16px; padding: 18px 20px;
    box-shadow: 0 14px 40px rgba(0,0,0,0.5);
}
.relnet-th-head { display: flex; align-items: center; gap: 14px; margin-bottom: 12px; }
.relnet-th-head img { width: 56px; height: 56px; border-radius: 50%; object-fit: cover; object-position: center top; border: 2px solid rgba(255,255,255,0.15); }
.relnet-th-name { font-family: 'Outfit', sans-serif; font-size: 19px; font-weight: 700; }
.relnet-th-role { font-size: 11.5px; font-weight: 500; color: var(--text-muted); font-style: normal; }
.relnet-th-mood { font-size: 12.5px; font-style: italic; color: var(--text-muted); }

.relnet-meters { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 18px; margin-bottom: 14px; }
.relnet-meter { min-width: 0; }
.relnet-meter span {
    display: flex; justify-content: space-between; align-items: baseline; gap: 6px;
    font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-muted);
}
.relnet-meter span b { font-size: 11.5px; color: var(--text-main); letter-spacing: 0; }
.relnet-meter-track { height: 7px; border-radius: 4px; background: rgba(255,255,255,0.08); overflow: hidden; margin-top: 3px; }
.relnet-meter-track i { display: block; height: 100%; border-radius: 4px; transition: width 0.5s ease; }

.relnet-th-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 9px; }
.relnet-th-list li {
    font-size: 13.5px; line-height: 1.5; color: var(--text-main);
    padding: 8px 12px; border-radius: 10px; background: rgba(255,255,255,0.03);
    border-left: 3px solid rgba(255,255,255,0.14);
}
.relnet-th-list li.tone-warm    { border-left-color: #3ad07a; }
.relnet-th-list li.tone-crush   { border-left-color: #ff5aa8; }
.relnet-th-list li.tone-jealous { border-left-color: #ff5555; }
.relnet-th-list li.tone-anguish { border-left-color: #5aa8ff; }
.relnet-th-list li.tone-cold    { border-left-color: #6b7280; }
.relnet-th-list li.tone-curious { border-left-color: #c9a06a; }
.relnet-th-list li.tone-steady  { border-left-color: #8fc7e8; }
.relnet-th-list li.tone-spite   { border-left-color: #ffd23d; background: rgba(255, 210, 61, 0.06); }

.relnet-foot { margin: 12px 0 0; font-size: 11.5px; color: var(--text-muted); text-align: center; opacity: 0.8; }

/* ---- pair inspector: the same pair read from both ends ---- */
.relnet-pair-head { text-align: center; margin-bottom: 14px; }
.relnet-pair-head h4 { font-family: 'Outfit', sans-serif; font-size: 17px; margin: 0 0 2px; }
.relnet-pair-head p { margin: 0; font-size: 12px; font-style: italic; color: var(--text-muted); }
.relnet-pair-cols { display: grid; grid-template-columns: 1fr; gap: 14px; }
@media (min-width: 520px) { .relnet-pair-cols { grid-template-columns: 1fr 1fr; } }
.relnet-pair-col {
    background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.07);
    border-radius: 12px; padding: 12px 14px;
}
.relnet-pair-col .relnet-meter { margin-bottom: 8px; }
.relnet-pair-dir {
    font-family: 'Outfit', sans-serif; font-weight: 700; font-size: 13px; margin-bottom: 10px;
}
.relnet-pair-dir span { color: var(--text-muted); font-weight: 500; }
.relnet-pair-thought {
    margin: 10px 0 0; font-size: 12.5px; line-height: 1.5; font-style: italic;
    color: var(--text-main); opacity: 0.9;
}
.relnet-back { display: block; margin: 16px auto 0; }

/* A confrontation option is a real fork in the voyage, so it gets more room
   than a plain nav button and its consequence line is part of the label. */
.confront-opt { text-align: left; width: 100%; }
.confront-opt .lf-lbl { font-weight: 700; }
.confront-opt:disabled { opacity: 0.45; cursor: not-allowed; }

/* ==========================================================================
   VOYAGE LOG (achievements)
   ========================================================================== */
.ach-head { text-align: center; color: var(--text-muted); font-size: 13px; margin-bottom: 14px; }
.ach-grid { display: grid; grid-template-columns: 1fr; gap: 10px; }
@media (min-width: 520px) { .ach-grid { grid-template-columns: 1fr 1fr; } }
.ach-card {
    display: flex; align-items: center; gap: 12px;
    padding: 12px 14px; border-radius: 12px;
    background: rgba(255,255,255,0.03);
    border: 1px solid rgba(255,255,255,0.07);
    text-align: left;
}
.ach-card.is-got { background: rgba(58, 208, 122, 0.08); border-color: rgba(58, 208, 122, 0.28); }
.ach-card.is-locked { opacity: 0.6; }
.ach-icon { font-size: 26px; flex-shrink: 0; width: 34px; text-align: center; }
.ach-name { font-family: 'Outfit', sans-serif; font-weight: 700; font-size: 14.5px; }
.ach-desc { font-size: 12px; color: var(--text-muted); }

/* ==========================================================================
   THE CAPTAIN'S LOCKER  (src/locker.js)

   Two states share one stylesheet on purpose: the locked render is the
   unlocked render with focus taken away from it. Nothing is hidden, nothing
   is replaced by an upsell placeholder — the player is looking at the real
   shelf through frosted glass, which is the only version of a teaser that
   does not feel like a lie.
   ========================================================================== */

.lk-head {
    /* Grid, not a heading with an absolutely-positioned stamp on top of it:
       the stamp used to sit over the title once the panel was narrow enough
       that "The Captain's Locker" reached the right edge. */
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: start;
    column-gap: 14px;
    padding: 4px 0 18px;
    border-bottom: 1px solid var(--border-soft);
    margin-bottom: 22px;
}

.lk-head h2 { grid-column: 1; }
.lk-sub { grid-column: 1; }

.lk-head h2 {
    font-family: 'Cinzel', serif;
    font-size: 1.5rem;
    letter-spacing: 0.5px;
    color: var(--gold);
    margin: 0 0 6px;
}

.lk-sub {
    color: var(--text-muted);
    font-size: 0.92rem;
    margin: 0;
    max-width: 62ch;
}

.lk-stamp {
    grid-column: 2;
    grid-row: 1;
    justify-self: end;
    font-size: 0.7rem;
    letter-spacing: 1.2px;
    color: var(--text-dim);
    border: 1px solid var(--border-soft);
    border-radius: 999px;
    padding: 3px 10px;
    white-space: nowrap;
}

.lk-section { margin-bottom: 34px; }

.lk-section h3 {
    font-family: 'Outfit', sans-serif;
    font-size: 1.05rem;
    color: var(--text-main);
    margin: 0 0 6px;
}

.lk-note {
    color: var(--text-dim);
    font-size: 0.85rem;
    line-height: 1.55;
    margin: 0 0 16px;
    max-width: 70ch;
}

/* ---- the cheats ---- */

.lk-band + .lk-band { margin-top: 22px; }

.lk-band-head {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0 0 10px;
    font-family: 'Outfit', sans-serif;
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--text-dim);
}

/* A hairline that runs out to the right edge, so the bands read as sections of
   one rack rather than three separate lists. */
.lk-band-head::after {
    content: "";
    flex: 1 1 auto;
    height: 1px;
    background: var(--border-soft);
}

.lk-band-n {
    order: 3;
    font-style: normal;
    letter-spacing: 0.06em;
    text-transform: none;
    font-size: 0.72rem;
    font-variant-numeric: tabular-nums;
    color: var(--text-dim);
}

.lk-grid {
    display: grid;
    /* min() so a narrow panel gets one honest column instead of one column
       300px wider than the panel it lives in. */
    grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
    gap: 12px;
}

.lk-card {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-areas: "icon body" "btn btn";
    gap: 12px;
    align-items: start;
    background: var(--card-bg);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-md);
    padding: 14px;
    transition: border-color 0.25s var(--ease-smooth), transform 0.25s var(--ease-smooth);
}

.lk-card.is-ready { border-color: rgba(230, 192, 101, 0.28); }
.lk-card.is-ready:hover { transform: translateY(-2px); }
.lk-card.is-cooling { opacity: 0.62; }

.lk-icon { grid-area: icon; font-size: 1.55rem; line-height: 1.2; }
.lk-body { grid-area: body; min-width: 0; }

.lk-name {
    font-family: 'Outfit', sans-serif;
    font-weight: 600;
    font-size: 0.98rem;
    color: var(--text-main);
    margin-bottom: 3px;
}

.lk-desc {
    font-size: 0.82rem;
    line-height: 1.5;
    color: var(--text-muted);
}

.lk-cd {
    margin-top: 8px;
    font-size: 0.72rem;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    color: var(--text-dim);
}

.lk-claim {
    grid-area: btn;
    margin-top: 12px;
    width: 100%;
    padding: 9px 14px;
    font-family: 'Outfit', sans-serif;
    font-weight: 600;
    font-size: 0.85rem;
    letter-spacing: 0.4px;
    color: var(--bg-void);
    background: linear-gradient(135deg, var(--gold), var(--amber));
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: filter 0.2s var(--ease-smooth);
}

.lk-claim:hover:not(:disabled) { filter: brightness(1.12); }

.lk-claim:disabled {
    background: rgba(255, 255, 255, 0.06);
    color: var(--text-dim);
    cursor: default;
}

/* ---- the shelf ----

   Cut into one section per person rather than one 73-tile wall. The filter
   chips above it are the fast path ("everything with Leysa in it"); the
   sections are the slow one (scroll and browse). Both read off the same
   Locker.shelves() grouping, so they cannot disagree. */

.lk-filters {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin: 0 0 18px;
    padding: 10px 0 14px;
    border-bottom: 1px solid var(--border-soft);
    /* The shelf is ~4000px of scroll. Sticking the chips to the top of the
       panel means switching from Noor to Leysa is one click from anywhere in
       it, instead of a scroll back to the top first. -1px hides the seam the
       browser leaves between a sticky element and the edge it sticks to. */
    position: sticky;
    top: -1px;
    z-index: 3;
    background: linear-gradient(180deg,
        var(--bg-deep) 0%, var(--bg-deep) 72%, rgba(10, 13, 22, 0.92) 100%);
    backdrop-filter: blur(8px);
}

.lk-filter {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 5px 12px 5px 5px;
    border: 1px solid var(--border-soft);
    border-radius: 999px;
    background: var(--card-bg);
    color: var(--text-muted);
    font-family: 'Outfit', sans-serif;
    font-size: 0.86rem;
    font-weight: 500;
    cursor: pointer;
    transition: border-color 0.2s var(--ease-smooth), color 0.2s var(--ease-smooth),
                background 0.2s var(--ease-smooth);
}

.lk-filter:hover { color: var(--text-main); border-color: rgba(255, 255, 255, 0.18); }

.lk-filter.is-on {
    color: var(--bg-void);
    background: linear-gradient(135deg, var(--gold), var(--amber));
    border-color: transparent;
    font-weight: 700;
}

.lk-filter:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

.lk-filter-face {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    object-fit: cover;
    /* The stills are framed for a card, not a 26px circle — pull focus up to
       where the face actually is. */
    object-position: 50% 22%;
    background: var(--bg-deep);
    flex: 0 0 auto;
}

.lk-filter-all {
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    color: var(--gold);
    border: 1px solid var(--border-soft);
}

.lk-filter.is-on .lk-filter-all { color: var(--bg-void); border-color: rgba(0, 0, 0, 0.25); }

.lk-filter-n {
    font-variant-numeric: tabular-nums;
    font-size: 0.74rem;
    opacity: 0.72;
}

.lk-shelf {
    display: grid;
    /* Was a hard 150px floor, which on a narrow panel produced one very wide
       column. min() lets the tile shrink with the panel instead. */
    grid-template-columns: repeat(auto-fill, minmax(min(150px, 100%), 1fr));
    gap: 10px;
}

.lk-shot {
    position: relative;
    margin: 0;
    border-radius: var(--radius-sm);
    overflow: hidden;
    border: 1px solid var(--border-soft);
    background: var(--bg-deep);
    cursor: pointer;
    aspect-ratio: 4 / 5;
}

.lk-thumb {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform 0.35s var(--ease-smooth), filter 0.35s var(--ease-smooth);
}

.lk-shot:not(.is-blurred):hover .lk-thumb { transform: scale(1.05); }

.lk-shot:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
}

/* The locked state. The blur is heavy enough that no frame is legible and
   light enough that the composition still reads as a real scene. */
.lk-shot.is-blurred .lk-thumb {
    filter: blur(15px) saturate(0.45) brightness(0.62);
    transform: scale(1.15);   /* hides the transparent edge blur leaves behind */
}

.lk-cap {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 16px 8px 7px;
    font-size: 0.72rem;
    line-height: 1.3;
    color: var(--text-main);
    background: linear-gradient(to top, rgba(4, 3, 8, 0.92), transparent);
}

.lk-shot.is-blurred .lk-cap {
    color: var(--text-dim);
    letter-spacing: 0.6px;
    text-transform: uppercase;
    font-size: 0.66rem;
}

.lk-padlock {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-size: 1.4rem;
    opacity: 0.5;
    pointer-events: none;
}

/* ---- the upsell strip under a locked panel ---- */

.lk-upsell {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    align-items: center;
    justify-content: space-between;
    padding: 18px 20px;
    border-radius: var(--radius-md);
    border: 1px solid rgba(230, 192, 101, 0.22);
    background: linear-gradient(135deg, rgba(230, 192, 101, 0.07), rgba(156, 43, 78, 0.07));
}

.lk-upsell-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.lk-upsell-text strong { font-family: 'Outfit', sans-serif; font-size: 0.98rem; color: var(--text-main); }
.lk-upsell-text span { font-size: 0.84rem; color: var(--text-muted); }

.lk-upsell-btn {
    flex: none;
    padding: 10px 20px;
    border-radius: var(--radius-sm);
    background: linear-gradient(135deg, var(--gold), var(--amber));
    color: var(--bg-void);
    font-family: 'Outfit', sans-serif;
    font-weight: 700;
    font-size: 0.86rem;
    text-decoration: none;
    white-space: nowrap;
}

.lk-upsell-btn:hover { filter: brightness(1.12); }

/* ---- the gate + the code prompt ---- */

.lk-gate,
.lk-viewer {
    position: fixed;
    inset: 0;
    z-index: 1200;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: rgba(4, 3, 8, 0.86);
    backdrop-filter: blur(14px);
    animation: overlayFade 0.3s var(--ease-smooth);
}

/* The access-code prompt on `online` builds is raised at launch, on top of the
   title screen (#titleScreen, z-index 5000). The regular gate's 1200 would
   render it BEHIND the title, so it appeared to spill below the menu.
   It also gets a SOLID, opaque backdrop: the shared overlayFade animation
   starts fully transparent, and if the tab is backgrounded mid-fade the title
   menu bleeds straight through. At launch the prompt must hide the menu
   outright, so it can't depend on the fade landing. */
.lk-gate.lk-code {
    z-index: 5500;
    background: #060409;
    backdrop-filter: blur(14px);
    animation: none;
}

.lk-gate-card {
    background: var(--card-bg-solid);
    border: 1px solid rgba(230, 192, 101, 0.22);
    border-radius: var(--radius-lg);
    max-width: 480px;
    width: 100%;
    padding: 30px;
    text-align: center;
    box-shadow: 0 30px 70px rgba(0, 0, 0, 0.5);
    animation: modalSlideUp 0.4s var(--ease-pop) forwards;
}

.lk-gate-glyph { font-size: 2rem; margin-bottom: 10px; }

.lk-gate-card h2 {
    font-family: 'Cinzel', serif;
    font-size: 1.3rem;
    color: var(--gold);
    margin: 0 0 12px;
}

.lk-gate-card p {
    font-size: 0.88rem;
    line-height: 1.6;
    color: var(--text-muted);
    margin: 0 0 16px;
}

.lk-gate-list {
    list-style: none;
    padding: 0;
    margin: 0 0 22px;
    text-align: left;
}

.lk-gate-list li {
    position: relative;
    padding-left: 22px;
    margin-bottom: 9px;
    font-size: 0.84rem;
    line-height: 1.5;
    color: var(--text-muted);
}

.lk-gate-list li::before {
    content: "\25C6";
    position: absolute;
    left: 2px;
    color: var(--gold);
    font-size: 0.7rem;
    top: 2px;
}

.lk-gate-cta {
    display: block;
    padding: 12px 22px;
    border-radius: var(--radius-sm);
    background: linear-gradient(135deg, var(--gold), var(--amber));
    color: var(--bg-void);
    font-family: 'Outfit', sans-serif;
    font-weight: 700;
    font-size: 0.92rem;
    text-decoration: none;
    border: none;
    width: 100%;
    cursor: pointer;
}

.lk-gate-cta:hover { filter: brightness(1.12); }

.lk-gate-back {
    display: block;
    width: 100%;
    margin-top: 10px;
    padding: 10px;
    background: none;
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-sm);
    color: var(--text-muted);
    font-size: 0.84rem;
    cursor: pointer;
}

.lk-gate-back:hover { color: var(--text-main); border-color: rgba(255, 255, 255, 0.18); }

.lk-code-input {
    width: 100%;
    padding: 12px 14px;
    margin-bottom: 10px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border-soft);
    background: rgba(0, 0, 0, 0.35);
    color: var(--text-main);
    font-family: 'Outfit', sans-serif;
    font-size: 1rem;
    letter-spacing: 2px;
    text-align: center;
}

.lk-code-input:focus {
    outline: none;
    border-color: var(--focus-ring);
}

.lk-code-err {
    color: var(--danger) !important;
    font-size: 0.8rem !important;
    margin: 0 0 12px !important;
}

/* ---- the clip viewer ---- */

.lk-viewer-card {
    position: relative;
    max-width: min(900px, 92vw);
    max-height: 88vh;
    border-radius: var(--radius-md);
    overflow: hidden;
    background: #000;
    box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6);
}

.lk-viewer-card video {
    display: block;
    max-width: 100%;
    max-height: 80vh;
}

.lk-viewer-cap {
    padding: 10px 14px;
    font-size: 0.85rem;
    color: var(--text-muted);
    background: var(--bg-deep);
}

.lk-viewer-close {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    border: none;
    background: rgba(0, 0, 0, 0.6);
    color: #fff;
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
}

.lk-viewer-close:hover { background: rgba(0, 0, 0, 0.85); }

/* ---- version stamps around the shell ---- */

.title-version {
    margin-top: 4px;
    font-size: 0.68rem;
    letter-spacing: 1.4px;
    color: var(--text-dim);
    opacity: 0.75;
}

.header-version {
    font-size: 0.62rem;
    letter-spacing: 1px;
    color: var(--text-dim);
    border: 1px solid var(--border-soft);
    border-radius: 999px;
    padding: 2px 8px;
    white-space: nowrap;
    align-self: center;
}

/* The locker tab reads as an aside, not a call to action. */
.tab-btn.tab-locker { color: var(--gold); }
.tab-btn.tab-locker.active { border-bottom-color: var(--gold); }

@media (prefers-reduced-motion: reduce) {
    .lk-card, .lk-thumb, .lk-gate-card { transition: none; animation: none; }
}

/* ==========================================================================
   CHARACTER DOSSIER — see src/dossier.js
   Reachable from the crew cards. Two halves: what you have worked out about
   this person, and what every number on screen actually means.
   ========================================================================== */
.player-dossier-btn {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    margin-top: 8px;
    padding: 6px 9px;
    border-radius: 9px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    background: rgba(255, 255, 255, 0.03);
    color: var(--text-muted);
    font-family: 'Outfit', sans-serif;
    font-size: 11px;
    letter-spacing: 0.3px;
    cursor: pointer;
    transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.player-dossier-btn:hover {
    background: rgba(255, 255, 255, 0.07);
    border-color: rgba(255, 255, 255, 0.18);
    color: var(--text-main);
}
.player-dossier-btn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.pd-icon { font-size: 12px; }
.pd-txt { flex: 1; text-align: left; }
.pd-count {
    font-weight: 700;
    color: var(--frost);
    font-variant-numeric: tabular-nums;
}

.dossier-card { max-width: 720px; text-align: left; }
.dossier-close { display: block; margin: 22px auto 0; padding: 10px 30px; }

.dsr-head {
    display: flex;
    align-items: center;
    gap: 16px;
    padding-bottom: 16px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.dsr-face {
    width: 74px; height: 74px;
    border-radius: 14px;
    object-fit: cover;
    border: 1px solid rgba(255, 255, 255, 0.12);
}
.dsr-id h3 { font-family: 'Outfit', sans-serif; font-size: 24px; margin: 0 0 2px; }
.dsr-role { font-size: 12px; color: var(--text-muted); margin: 0 0 4px; }
.dsr-mood { font-size: 13px; color: var(--text-dim); font-style: italic; margin: 0; }

.dsr-block { margin-top: 24px; }
.dsr-block h4 {
    font-family: 'Outfit', sans-serif;
    font-size: 13px;
    text-transform: uppercase;
    letter-spacing: 1.6px;
    color: var(--text-muted);
    margin: 0 0 10px;
}
.dsr-count { color: var(--frost); letter-spacing: 0; font-variant-numeric: tabular-nums; }
.dsr-hint { font-size: 11.5px; color: var(--text-dim); margin: 0 0 12px; line-height: 1.5; }
.dsr-empty { font-size: 13px; color: var(--text-dim); font-style: italic; margin: 0 0 10px; }

.dsr-facts { list-style: none; padding: 0; margin: 0 0 10px; }
.dsr-facts li {
    position: relative;
    padding: 7px 0 7px 22px;
    font-size: 13.5px;
    line-height: 1.5;
    border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}
.dsr-facts li::before {
    content: "◆";
    position: absolute;
    left: 2px;
    color: var(--frost);
    font-size: 9px;
    top: 11px;
}

/* One variable: name, scope, value, bar, what it is, what moves it. */
.dsr-var {
    padding: 12px 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.dsr-var-head { display: flex; align-items: baseline; gap: 8px; }
.dsr-var-name { font-family: 'Outfit', sans-serif; font-size: 14px; font-weight: 700; }
.dsr-var-scope {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.8px;
    color: var(--text-dim);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 999px;
    padding: 1px 7px;
}
.dsr-var-val {
    margin-left: auto;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    font-size: 14px;
}
.dsr-var-val i { font-style: normal; color: var(--text-dim); font-weight: 400; font-size: 11px; }

.dsr-track {
    height: 5px;
    border-radius: 3px;
    background: rgba(255, 255, 255, 0.08);
    overflow: hidden;
    margin: 7px 0 8px;
}
.dsr-track i { display: block; height: 100%; border-radius: 3px; transition: width 0.4s ease; }

.dsr-var-what { font-size: 13px; line-height: 1.55; margin: 0 0 6px; color: var(--text-main); }
.dsr-var-move { display: flex; flex-wrap: wrap; gap: 6px 18px; margin: 0; font-size: 11.5px; line-height: 1.45; }
.dsr-up { color: #7fdca6; flex: 1 1 240px; }
.dsr-down { color: #e89a9a; flex: 1 1 240px; }

.dsr-thoughts { list-style: none; padding: 0; margin: 0; }
.dsr-thoughts li {
    padding: 7px 0;
    font-size: 13px;
    line-height: 1.5;
    border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}

@media (max-width: 560px) {
    .dsr-head { flex-direction: column; align-items: flex-start; }
    .dsr-var-move { flex-direction: column; gap: 4px; }
}

/* The Sway buttons now state their outcome and who will witness it, so they
   need room for two extra lines. See renderSwayOptions in ui.js. */
.sway-outcome {
    display: block;
    width: 100%;
    margin-top: 5px;
    font-size: 11px;
    line-height: 1.4;
    color: var(--text-main);
    white-space: normal;
}
.sway-seen {
    display: block;
    width: 100%;
    margin-top: 3px;
    font-size: 10.5px;
    line-height: 1.35;
    color: var(--text-dim);
    font-style: italic;
    white-space: normal;
}

/* ==========================================================================
   START OF VOYAGE — see src/setup.js
   ========================================================================== */
.setup-card { max-width: 640px; text-align: left; }
.setup-card h2 {
    font-family: 'Outfit', sans-serif;
    font-size: 24px;
    margin: 0 0 22px;
    text-align: center;
}
.setup-block { margin-bottom: 26px; }
.setup-block h3 {
    font-family: 'Outfit', sans-serif;
    font-size: 13px;
    text-transform: uppercase;
    letter-spacing: 1.6px;
    color: var(--text-muted);
    margin: 0 0 6px;
}
.setup-hint { font-size: 11.5px; color: var(--text-dim); line-height: 1.5; margin: 0 0 12px; }

.setup-cast { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.setup-cast-btn {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 12px 6px;
    border-radius: 12px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    background: rgba(255, 255, 255, 0.03);
    cursor: pointer;
    transition: border-color 0.2s ease, background 0.2s ease, transform 0.2s var(--ease-pop);
}
.setup-cast-btn:hover { background: rgba(255, 255, 255, 0.07); transform: translateY(-2px); }
.setup-cast-btn.is-sel {
    border-color: var(--frost);
    background: rgba(143, 199, 232, 0.12);
    box-shadow: 0 0 0 1px rgba(143, 199, 232, 0.3);
}
.setup-cast-btn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.setup-cast-face {
    width: 56px; height: 56px;
    border-radius: 10px;
    object-fit: cover;
    border: 1px solid rgba(255, 255, 255, 0.1);
}
.setup-cast-name { font-family: 'Outfit', sans-serif; font-weight: 700; font-size: 14px; }
.setup-cast-rel { font-size: 10px; color: var(--text-dim); text-align: center; line-height: 1.3; }

.setup-content { display: flex; flex-direction: column; gap: 8px; }
.setup-toggle {
    display: flex;
    align-items: flex-start;
    gap: 11px;
    padding: 11px 13px;
    border-radius: 11px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    background: rgba(255, 255, 255, 0.02);
    cursor: pointer;
    text-align: left;
    transition: border-color 0.2s ease, background 0.2s ease;
}
.setup-toggle:hover { background: rgba(255, 255, 255, 0.06); }
.setup-toggle:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.setup-toggle.is-on { border-color: rgba(58, 208, 122, 0.4); background: rgba(58, 208, 122, 0.07); }
.setup-toggle-box {
    flex: 0 0 auto;
    width: 19px; height: 19px;
    border-radius: 5px;
    border: 1px solid rgba(255, 255, 255, 0.22);
    display: flex; align-items: center; justify-content: center;
    font-size: 12px;
    color: #3ad07a;
    margin-top: 1px;
}
.setup-toggle.is-on .setup-toggle-box { border-color: rgba(58, 208, 122, 0.6); }
.setup-toggle-txt { display: flex; flex-direction: column; gap: 2px; }
.setup-toggle-name { font-family: 'Outfit', sans-serif; font-size: 13.5px; font-weight: 600; }
.setup-toggle-note { font-size: 11.5px; color: var(--text-dim); line-height: 1.45; }

.setup-prologue { padding: 4px 0 8px; }
.setup-beat {
    font-size: 15px;
    line-height: 1.7;
    margin: 0 0 16px;
    color: var(--text-main);
    opacity: 0;
    transform: translateY(6px);
}
.setup-beat.is-in { animation: beatIn 0.55s var(--ease-smooth) forwards; }
.setup-beat.is-in:nth-child(2) { animation-delay: 0.18s; }
.setup-beat.is-in:nth-child(3) { animation-delay: 0.36s; }
.setup-beat.is-in:nth-child(4) { animation-delay: 0.54s; }
@keyframes beatIn { to { opacity: 1; transform: none; } }

/* Motion off: no staggered reveal, just the text. */
html[data-motion="none"] .setup-beat,
html[data-motion="reduced"] .setup-beat { opacity: 1; transform: none; animation: none; }

.setup-actions {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: 12px;
    margin-top: 8px;
    padding-top: 18px;
    border-top: 1px solid rgba(255, 255, 255, 0.07);
}
.setup-actions .btn-harem { padding: 10px 28px; }

@media (max-width: 560px) {
    .setup-cast { grid-template-columns: repeat(2, 1fr); }
}

/* "Who is watching" strip, shown above the action list. See renderWitnessStrip. */
.witness-strip {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px 9px;
    margin-bottom: 12px;
    padding: 8px 12px;
    border-radius: 10px;
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid rgba(255, 255, 255, 0.07);
    font-size: 12px;
}
.wit-eye { opacity: 0.65; font-size: 12px; }
.wit-txt { color: var(--text-muted); letter-spacing: 0.3px; }
.wit-name { font-weight: 700; display: inline-flex; align-items: baseline; gap: 4px; }
.wit-sting {
    font-style: normal;
    font-size: 10.5px;
    font-weight: 700;
    color: #ff8a3d;
    background: rgba(255, 138, 61, 0.13);
    border-radius: 999px;
    padding: 1px 6px;
}
/* ===================================================================
   CONTRACTS — what this voyage is for. Setup picker + the HUD objective.
   =================================================================== */
.setup-contract-list { display: flex; flex-direction: column; gap: 6px; }
.setup-contract {
    display: grid; grid-template-columns: 1fr; gap: 2px;
    text-align: left; cursor: pointer;
    padding: 9px 12px; border-radius: 10px;
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid rgba(255, 255, 255, 0.08);
    color: inherit;
}
.setup-contract:hover { border-color: rgba(255, 217, 122, 0.35); }
.setup-contract.is-sel {
    border-color: rgba(255, 217, 122, 0.65);
    background: rgba(255, 217, 122, 0.08);
}
.setup-contract-name { font-size: 13px; font-weight: 800; letter-spacing: .2px; }
.setup-contract-style { font-size: 10.5px; font-weight: 600; color: #ffd97a; opacity: .8; letter-spacing: .3px; margin-left: 6px; text-transform: uppercase; }
.setup-contract-goal { font-size: 11.5px; color: var(--text-muted); line-height: 1.35; }
.setup-contract-fail { font-size: 10.5px; color: #e8a06a; line-height: 1.35; }

/* The objective line in the voyage strip. Absent entirely when Adrift. */
.voy-goal {
    flex-basis: 100%;
    display: flex; align-items: center; gap: 6px;
    font-size: 11px; font-weight: 700; letter-spacing: .2px;
    color: #ffd97a;
    padding-bottom: 2px;
}
.voy-goal-g { font-style: normal; opacity: .85; }

/* NO scroll-behavior HERE, deliberately.
   Decision boxes scroll themselves into view (see UI.buildChoiceBox). Smooth
   scrolling was tried in both forms and measured broken in the shipped shell:
   with scroll-behavior:smooth on this container, setting scrollTop landed at 0;
   with it forced to auto, the identical assignment landed at 411, its correct
   clamped maximum. An animation that eats the scroll is worse than no
   animation, so the jump stands. */

/* ===================================================================
   THE MORNING BOARD — the half of the day that is all decision.
   Draws inside the arena nav panel, same host as the landing party.
   =================================================================== */
/* .landfall-box is a wrapping flex ROW (it was built for two or three landing
   party buttons side by side). The morning board is a whole panel, not a
   button, so it and its footer have to claim the full width or they end up
   laid out beside each other with the grid squeezed into half the space. */
.landfall-box > .day-board,
.landfall-box > .day-foot { flex: 1 0 100%; }

.day-board { display: flex; flex-direction: column; gap: 10px; margin-top: 6px; }

.day-bench {
    display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
    padding: 8px 10px; border-radius: 10px;
    background: rgba(255, 255, 255, 0.035);
    border: 1px solid rgba(255, 255, 255, 0.07);
}
.day-bench-lead { font-size: 11px; color: var(--text-muted); margin-right: 4px; }

.day-chip {
    --chip: #fff;
    display: inline-flex; align-items: center; gap: 4px;
    padding: 3px 9px; border-radius: 999px; cursor: pointer;
    font-size: 11.5px; font-weight: 700; color: var(--chip);
    background: color-mix(in srgb, var(--chip) 12%, transparent);
    border: 1px solid color-mix(in srgb, var(--chip) 38%, transparent);
    transition: transform .12s ease, box-shadow .12s ease;
}
.day-chip:hover { transform: translateY(-1px); }
.day-chip.is-picking {
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--chip) 70%, transparent);
    transform: translateY(-1px);
}
.day-chip-note { font-style: normal; font-size: 11px; }

/* Seven posts have to be readable WITHOUT scrolling the play panel, which is
   only ~570px tall. Measured at 190px minimum column width the board came out
   834px high and the player had to scroll to find the button that starts the
   day — so the columns are narrower and the cards are laid out horizontally. */
.day-grid {
    display: grid; gap: 6px;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.day-post {
    display: flex; flex-direction: column; gap: 4px;
    padding: 7px 8px; border-radius: 9px;
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid rgba(255, 255, 255, 0.07);
}
.day-post.is-manned { border-color: rgba(255, 217, 122, 0.30); background: rgba(255, 217, 122, 0.05); }
.day-post.is-full { opacity: 0.5; }

.day-post-head {
    display: grid; grid-template-columns: auto 1fr; gap: 1px 7px;
    align-items: baseline; text-align: left; width: 100%;
    background: none; border: 0; padding: 0; cursor: pointer; color: inherit;
}
.day-post-g { grid-row: span 2; font-size: 15px; }
.day-post-n { font-size: 12px; font-weight: 700; letter-spacing: .2px; }
.day-post-d { font-size: 10px; color: var(--text-muted); line-height: 1.3; }
.day-rig { font-style: normal; margin-left: 5px; font-size: 11px; }
.day-rig.is-none   { opacity: .25; filter: grayscale(1); }
.day-rig.is-broken { opacity: .6;  filter: grayscale(.7); }

.day-post-crew { display: flex; flex-wrap: wrap; gap: 4px; min-height: 22px; align-items: center; }
.day-slot {
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 20px; border-radius: 999px;
    font-size: 11px; color: var(--text-dim);
    border: 1px dashed rgba(255, 255, 255, 0.16);
}
.day-post-eff { font-size: 10px; color: #8fd4a8; line-height: 1.3; }

/* Sticks to the bottom of the board so "Start the day" is always reachable
   even when the panel does have to scroll. */
.day-foot {
    display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap;
    position: sticky; bottom: 0; z-index: 2;
    padding-top: 8px;
    background: linear-gradient(to top, var(--card-bg-solid) 62%, transparent);
}
.day-auto { font-size: 12px; }
.day-go { flex: 1; min-width: 150px; }

/* The row of things that are yours to decide and cost no Sway, because
   influence is not what they are. Ending your own relationship lives here. */
.personal-panel { margin-top: 6px; }
.sway-btn.sway-grim {
    border-color: rgba(255, 77, 77, 0.35);
    background: rgba(255, 77, 77, 0.07);
}
.sway-btn.sway-grim .sway-btn-label { color: #ff8a8a; }
.sway-btn.sway-grim:hover { border-color: rgba(255, 77, 77, 0.6); }

/* The ache: no partner in the round, but they wanted somebody who is. A real
   cost, and a quieter one than a partner watching — so it reads cooler. */
.wit-ache {
    color: #5aa8ff;
    background: rgba(90, 168, 255, 0.13);
}
.wit-warn { color: #e8a06a; flex-basis: 100%; font-size: 11px; line-height: 1.4; }
.wit-calm { color: var(--text-dim); flex-basis: 100%; font-size: 11px; }

/* How much somebody has had, on their crew card. */
.player-drunk-note {
    margin-top: 5px;
    font-size: 11px;
    color: #ffb03d;
    letter-spacing: 0.2px;
}

/* Ashore (haven.js). The villa has its own footage now (islas/interior/), so
   the land tint only warms it a little instead of disguising a sea plate. It
   wins over the tide colour the backdrop sets inline, hence !important. */
body.is-ashore .bg-tint {
    background: linear-gradient(180deg, rgba(214, 160, 92, 0.30) 0%, rgba(96, 120, 70, 0.34) 100%) !important;
    opacity: 0.22 !important;
}
body.is-ashore #tidePhaseName { color: #e8c38a; }
